abicoder 0.0.1 → 0.1.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.
- checksums.yaml +4 -4
- data/Manifest.txt +4 -0
- data/README.md +131 -2
- data/Rakefile +8 -1
- data/lib/abicoder/decoder.rb +208 -0
- data/lib/abicoder/encoder.rb +279 -0
- data/lib/abicoder/parser.rb +158 -0
- data/lib/abicoder/types.rb +155 -0
- data/lib/abicoder/version.rb +1 -1
- data/lib/abicoder.rb +53 -1
- metadata +12 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3bc1e75e6080a55ae3447f7cfd3630ed587edf021658dd9159584fbb3ef28743
|
4
|
+
data.tar.gz: 84d27e90d278d0c2fc84db605005735164b4a63c084c087777218eadadb0044b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 597f54c3c8e21027bb10d326c7c6763653d752ad3285abc1c2086023d17c6dc9207d8e99f16a008265bff3a2ad61e1f261db501911c46df56a509660102dc019
|
7
|
+
data.tar.gz: b776e32b5afd57e233aef6ffb87dba66b65272533f60f635c371e0a199fad593a1bd55cd6248f39a3002849b66e77ecf1bb7bd06f3ccc453cc87854d0c77d059
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Application Binary Inteface (ABI) Coder For Ethereum & Co.
|
2
2
|
|
3
|
-
abicoder - application binary interface (abi) encoding / decoding machinery / helper for Ethereum & Co. (blockchain) contracts
|
3
|
+
abicoder - "lite" application binary interface (abi) encoding / decoding machinery / helper for Ethereum & Co. (blockchain) contracts with zero-dependencies for easy (re)use
|
4
4
|
|
5
5
|
|
6
6
|
* home :: [github.com/rubycocos/blockchain](https://github.com/rubycocos/blockchain)
|
@@ -12,7 +12,136 @@ abicoder - application binary interface (abi) encoding / decoding machinery / he
|
|
12
12
|
|
13
13
|
## Usage
|
14
14
|
|
15
|
-
|
15
|
+
|
16
|
+
### Encode & Decode Types (Contract Function Call Data)
|
17
|
+
|
18
|
+
|
19
|
+
``` ruby
|
20
|
+
require 'abicode'
|
21
|
+
|
22
|
+
#####
|
23
|
+
# try ABI.encode
|
24
|
+
|
25
|
+
## Encoding simple types
|
26
|
+
types = [ 'uint256', 'string' ]
|
27
|
+
args = [ 1234, 'Hello World' ]
|
28
|
+
ABI.encode( types, args ) # returns binary blob / string
|
29
|
+
#=> hex"00000000000000000000000000000000000000000000000000000000000004d2"+
|
30
|
+
# "0000000000000000000000000000000000000000000000000000000000000040"+
|
31
|
+
# "000000000000000000000000000000000000000000000000000000000000000b"+
|
32
|
+
# "48656c6c6f20576f726c64000000000000000000000000000000000000000000"
|
33
|
+
|
34
|
+
## Encoding with arrays types
|
35
|
+
types = [ 'uint256[]', 'string' ]
|
36
|
+
args = [ [1234, 5678] , 'Hello World' ]
|
37
|
+
ABI.encode( types, args ) # returns binary blob / string
|
38
|
+
#=> hex"0000000000000000000000000000000000000000000000000000000000000040"+
|
39
|
+
# "00000000000000000000000000000000000000000000000000000000000000a0"+
|
40
|
+
# "0000000000000000000000000000000000000000000000000000000000000002"+
|
41
|
+
# "00000000000000000000000000000000000000000000000000000000000004d2"+
|
42
|
+
# "000000000000000000000000000000000000000000000000000000000000162e"+
|
43
|
+
# "000000000000000000000000000000000000000000000000000000000000000b"+
|
44
|
+
# "48656c6c6f20576f726c64000000000000000000000000000000000000000000"
|
45
|
+
|
46
|
+
## Encoding complex structs (also known as tuples)
|
47
|
+
types = [ 'uint256', '(uint256,string)']
|
48
|
+
args = [1234, [5678, 'Hello World']]
|
49
|
+
ABI.encode( types, args ) # returns binary blob / string
|
50
|
+
#=> hex'00000000000000000000000000000000000000000000000000000000000004d2'+
|
51
|
+
# '0000000000000000000000000000000000000000000000000000000000000040'+
|
52
|
+
# '000000000000000000000000000000000000000000000000000000000000162e'+
|
53
|
+
# '0000000000000000000000000000000000000000000000000000000000000040'+
|
54
|
+
# '000000000000000000000000000000000000000000000000000000000000000b'+
|
55
|
+
# '48656c6c6f20576f726c64000000000000000000000000000000000000000000'
|
56
|
+
|
57
|
+
|
58
|
+
#####
|
59
|
+
# try ABI.decode
|
60
|
+
|
61
|
+
## Decoding simple types
|
62
|
+
types = [ 'uint256', 'string' ]
|
63
|
+
data = hex'00000000000000000000000000000000000000000000000000000000000004d2'+
|
64
|
+
'0000000000000000000000000000000000000000000000000000000000000040'+
|
65
|
+
'000000000000000000000000000000000000000000000000000000000000000b'+
|
66
|
+
'48656c6c6f20576f726c64000000000000000000000000000000000000000000'
|
67
|
+
ABI.decode( types, data) # returns args
|
68
|
+
#=> [1234, "Hello World"]
|
69
|
+
|
70
|
+
|
71
|
+
## Decoding with arrays types
|
72
|
+
types = [ 'uint256[]', 'string' ]
|
73
|
+
data = hex'0000000000000000000000000000000000000000000000000000000000000040'+
|
74
|
+
'00000000000000000000000000000000000000000000000000000000000000a0'+
|
75
|
+
'0000000000000000000000000000000000000000000000000000000000000002'+
|
76
|
+
'00000000000000000000000000000000000000000000000000000000000004d2'+
|
77
|
+
'000000000000000000000000000000000000000000000000000000000000162e'+
|
78
|
+
'000000000000000000000000000000000000000000000000000000000000000b'+
|
79
|
+
'48656c6c6f20576f726c64000000000000000000000000000000000000000000'
|
80
|
+
ABI.decode( types, data ) # returns args
|
81
|
+
#=> [[1234, 5678], "Hello World"]
|
82
|
+
|
83
|
+
|
84
|
+
## Decoding complex structs (also known as tuples)
|
85
|
+
types = [ 'uint256', '(uint256,string)']
|
86
|
+
data = hex'00000000000000000000000000000000000000000000000000000000000004d2'+
|
87
|
+
'0000000000000000000000000000000000000000000000000000000000000040'+
|
88
|
+
'000000000000000000000000000000000000000000000000000000000000162e'+
|
89
|
+
'0000000000000000000000000000000000000000000000000000000000000040'+
|
90
|
+
'000000000000000000000000000000000000000000000000000000000000000b'+
|
91
|
+
'48656c6c6f20576f726c64000000000000000000000000000000000000000000'
|
92
|
+
ABI.decode( types, data ) # returns args
|
93
|
+
#=> [1234, [5678, "Hello World"]]
|
94
|
+
```
|
95
|
+
|
96
|
+
and so on.
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
## Reference
|
103
|
+
|
104
|
+
For the full (and latest) official application
|
105
|
+
binary inteface (abi) specification
|
106
|
+
see the [**Contract ABI Specification**](https://docs.soliditylang.org/en/develop/abi-spec.html).
|
107
|
+
|
108
|
+
|
109
|
+
### Types
|
110
|
+
|
111
|
+
The following elementary types are supported:
|
112
|
+
|
113
|
+
- `uint<M>`: unsigned integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`. e.g. `uint32`, `uint8`, `uint256`.
|
114
|
+
- `int<M>`: two's complement signed integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`.
|
115
|
+
- `address`: equivalent to `uint160`, except for the assumed interpretation and language typing.
|
116
|
+
For computing the function selector, `address` is used.
|
117
|
+
- `bool`: equivalent to `uint8` restricted to the values 0 and 1. For computing the function selector, `bool` is used.
|
118
|
+
- `bytes<M>`: binary type of `M` bytes, `0 < M <= 32`.
|
119
|
+
|
120
|
+
|
121
|
+
The following (fixed-size) array types are supported:
|
122
|
+
|
123
|
+
- `<type>[M]`: a fixed-length array of `M` elements, `M >= 0`, of the given type.
|
124
|
+
|
125
|
+
<!--
|
126
|
+
Note: While this ABI specification can express fixed-length arrays with zero elements,
|
127
|
+
they're not supported by the compiler.
|
128
|
+
-->
|
129
|
+
|
130
|
+
The following non-fixed-size types are supported:
|
131
|
+
|
132
|
+
- `bytes`: dynamic sized byte sequence.
|
133
|
+
- `string`: dynamic sized unicode string assumed to be UTF-8 encoded.
|
134
|
+
- `<type>[]`: a variable-length array of elements of the given type.
|
135
|
+
|
136
|
+
Types can be combined to a tuple by enclosing them inside parentheses, separated by commas:
|
137
|
+
|
138
|
+
- `(T1,T2,...,Tn)`: tuple consisting of the types `T1`, ..., `Tn`, `n >= 0`
|
139
|
+
|
140
|
+
It is possible to form tuples of tuples, arrays of tuples and so on.
|
141
|
+
|
142
|
+
<!--
|
143
|
+
It is also possible to form zero-tuples (where `n == 0`).
|
144
|
+
-->
|
16
145
|
|
17
146
|
|
18
147
|
|
data/Rakefile
CHANGED
@@ -2,11 +2,18 @@ require 'hoe'
|
|
2
2
|
require './lib/abicoder/version.rb'
|
3
3
|
|
4
4
|
|
5
|
+
###
|
6
|
+
# hack/ quick fix for broken intuit_values - overwrite with dummy
|
7
|
+
class Hoe
|
8
|
+
def intuit_values( input ); end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
5
12
|
Hoe.spec 'abicoder' do
|
6
13
|
|
7
14
|
self.version = ABICoder::VERSION
|
8
15
|
|
9
|
-
self.summary = "abicoder - application binary interface (abi) encoding / decoding machinery / helper for Ethereum & Co. (blockchain) contracts"
|
16
|
+
self.summary = "abicoder - 'lite' application binary interface (abi) encoding / decoding machinery / helper for Ethereum & Co. (blockchain) contracts with zero-dependencies for easy (re)use"
|
10
17
|
self.description = summary
|
11
18
|
|
12
19
|
self.urls = { home: 'https://github.com/rubycocos/blockchain' }
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module ABI
|
2
|
+
|
3
|
+
|
4
|
+
class Decoder
|
5
|
+
|
6
|
+
##
|
7
|
+
# Decodes multiple arguments using the head/tail mechanism.
|
8
|
+
#
|
9
|
+
def decode( types, data, raise_errors = false )
|
10
|
+
## for convenience check if types is a String
|
11
|
+
## otherwise assume ABI::Type already
|
12
|
+
types = types.map { |type| type.is_a?( Type ) ? type : Type.parse( type ) }
|
13
|
+
|
14
|
+
outputs = [nil] * types.size
|
15
|
+
start_positions = [nil] * types.size + [data.size]
|
16
|
+
|
17
|
+
# TODO: refactor, a reverse iteration will be better
|
18
|
+
pos = 0
|
19
|
+
types.each_with_index do |t, i|
|
20
|
+
# If a type is static, grab the data directly, otherwise record its
|
21
|
+
# start position
|
22
|
+
if t.dynamic?
|
23
|
+
|
24
|
+
if pos>data.size-1
|
25
|
+
if raise_errors
|
26
|
+
raise DecodingError, "Position out of bounds #{pos}>#{data.size-1}"
|
27
|
+
else
|
28
|
+
puts "!! WARN - DecodingError: Position out of bounds #{pos}>#{data.size-1}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
start_positions[i] = big_endian_to_int(data[pos, 32])
|
33
|
+
|
34
|
+
if start_positions[i]>data.size-1
|
35
|
+
if raise_errors
|
36
|
+
raise DecodingError, "Start position out of bounds #{start_positions[i]}>#{data.size-1}"
|
37
|
+
else
|
38
|
+
puts "!! WARN - DecodingError: Start position out of bounds #{start_positions[i]}>#{data.size-1}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
j = i - 1
|
44
|
+
while j >= 0 && start_positions[j].nil?
|
45
|
+
start_positions[j] = start_positions[i]
|
46
|
+
j -= 1
|
47
|
+
end
|
48
|
+
|
49
|
+
pos += 32
|
50
|
+
else
|
51
|
+
## puts "step 1 - decode item [#{i}] - #{t.format} size: #{t.size} dynamic? #{t.dynamic?}"
|
52
|
+
|
53
|
+
outputs[i] = zero_padding( data, pos, t.size, start_positions )
|
54
|
+
pos += t.size
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
# We add a start position equal to the length of the entire data for
|
61
|
+
# convenience.
|
62
|
+
j = types.size - 1
|
63
|
+
while j >= 0 && start_positions[j].nil?
|
64
|
+
start_positions[j] = start_positions[types.size]
|
65
|
+
j -= 1
|
66
|
+
end
|
67
|
+
|
68
|
+
if pos > data.size
|
69
|
+
if raise_errors
|
70
|
+
raise DecodingError, "Not enough data for head"
|
71
|
+
else
|
72
|
+
puts "!! WARN - DecodingError: Not enough data for head"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
types.each_with_index do |t, i|
|
78
|
+
if t.dynamic?
|
79
|
+
offset, next_offset = start_positions[i, 2]
|
80
|
+
if offset<=data.size && next_offset<=data.size
|
81
|
+
outputs[i] = data[offset...next_offset]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if outputs.include?(nil)
|
87
|
+
if raise_errors
|
88
|
+
raise DecodingError, "Not all data can be parsed"
|
89
|
+
else
|
90
|
+
puts "!! WARN: DecodingError - Not all data can be parsed"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
types.zip(outputs).map do |(t, out)|
|
96
|
+
## puts "step 2 - decode item - #{t.format} got: #{out.size} byte(s) - size: #{t.size} dynamic? #{t.dynamic?}"
|
97
|
+
|
98
|
+
decode_type(t, out)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
def decode_type( type, arg )
|
105
|
+
return nil if arg.nil? || arg.empty?
|
106
|
+
|
107
|
+
|
108
|
+
if type.is_a?( String ) || type.is_a?( Bytes )
|
109
|
+
l = big_endian_to_int( arg[0,32] )
|
110
|
+
data = arg[32..-1]
|
111
|
+
data[0, l]
|
112
|
+
elsif type.is_a?( Tuple )
|
113
|
+
arg ? decode(type.types, arg) : []
|
114
|
+
elsif type.is_a?( FixedArray ) # static-sized arrays
|
115
|
+
l = type.dim
|
116
|
+
subtype = type.subtype
|
117
|
+
if subtype.dynamic?
|
118
|
+
start_positions = (0...l).map {|i| big_endian_to_int(arg[32*i, 32]) }
|
119
|
+
start_positions.push arg.size
|
120
|
+
|
121
|
+
outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
|
122
|
+
|
123
|
+
outputs.map {|out| decode_type(subtype, out) }
|
124
|
+
else
|
125
|
+
(0...l).map {|i| decode_type(subtype, arg[subtype.size*i, subtype.size]) }
|
126
|
+
end
|
127
|
+
elsif type.is_a?( Array )
|
128
|
+
l = big_endian_to_int( arg[0,32] )
|
129
|
+
raise DecodingError, "Too long length: #{l}" if l > 100000
|
130
|
+
subtype = type.subtype
|
131
|
+
|
132
|
+
if subtype.dynamic?
|
133
|
+
raise DecodingError, "Not enough data for head" unless arg.size >= 32 + 32*l
|
134
|
+
|
135
|
+
start_positions = (1..l).map {|i| 32+big_endian_to_int(arg[32*i, 32]) }
|
136
|
+
start_positions.push arg.size
|
137
|
+
|
138
|
+
outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
|
139
|
+
|
140
|
+
outputs.map {|out| decode_type(subtype, out) }
|
141
|
+
else
|
142
|
+
(0...l).map {|i| decode_type(subtype, arg[32 + subtype.size*i, subtype.size]) }
|
143
|
+
end
|
144
|
+
else
|
145
|
+
decode_primitive_type( type, arg )
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
def decode_primitive_type(type, data)
|
151
|
+
case type
|
152
|
+
when Address
|
153
|
+
encode_hex( data[12..-1] )
|
154
|
+
when String, Bytes
|
155
|
+
if data.length == 32
|
156
|
+
data[0..32]
|
157
|
+
else
|
158
|
+
size = big_endian_to_int( data[0,32] )
|
159
|
+
data[32..-1][0,size]
|
160
|
+
end
|
161
|
+
when FixedBytes
|
162
|
+
data[0, type.length]
|
163
|
+
when Uint
|
164
|
+
big_endian_to_int( data )
|
165
|
+
when Int
|
166
|
+
u = big_endian_to_int( data )
|
167
|
+
u >= 2**(type.bits-1) ? (u - 2**type.bits) : u
|
168
|
+
when Bool
|
169
|
+
data[-1] == BYTE_ONE
|
170
|
+
else
|
171
|
+
raise DecodingError, "Unknown primitive type: #{type.class.name} #{type.format}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
def zero_padding( data, pos, count, start_positions )
|
177
|
+
if pos >= data.size
|
178
|
+
start_positions[start_positions.size-1] += count
|
179
|
+
"\x00"*count
|
180
|
+
elsif pos + count > data.size
|
181
|
+
start_positions[start_positions.size-1] += ( count - (data.size-pos))
|
182
|
+
data[pos,data.size-pos] + "\x00"*( count - (data.size-pos))
|
183
|
+
else
|
184
|
+
data[pos, count]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
###########
|
190
|
+
# decoding helpers / utils
|
191
|
+
|
192
|
+
def big_endian_to_int( bin )
|
193
|
+
bin = bin.sub( /\A(\x00)+/, '' ) ## keep "performance" shortcut - why? why not?
|
194
|
+
### todo/check - allow nil - why? why not?
|
195
|
+
## raise DeserializationError, "Invalid serialization (not minimal length)" if !@size && serial.size > 0 && serial[0] == BYTE_ZERO
|
196
|
+
bin = bin || BYTE_ZERO
|
197
|
+
bin.unpack("H*").first.to_i(16)
|
198
|
+
end
|
199
|
+
|
200
|
+
def encode_hex( bin ) ## bin_to_hex
|
201
|
+
raise TypeError, "Value must be a String" unless bin.is_a?(::String)
|
202
|
+
bin.unpack("H*").first
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
end # class Decoder
|
207
|
+
|
208
|
+
end ## module ABI
|
@@ -0,0 +1,279 @@
|
|
1
|
+
|
2
|
+
module ABI
|
3
|
+
##
|
4
|
+
# Contract ABI encoding and decoding.
|
5
|
+
#
|
6
|
+
# @see https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
|
7
|
+
#
|
8
|
+
|
9
|
+
class EncodingError < StandardError; end
|
10
|
+
class DecodingError < StandardError; end
|
11
|
+
class ValueError < StandardError; end
|
12
|
+
|
13
|
+
class ValueOutOfBounds < ValueError; end
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
class Encoder
|
18
|
+
|
19
|
+
##
|
20
|
+
# Encodes multiple arguments using the head/tail mechanism.
|
21
|
+
#
|
22
|
+
def encode( types, args )
|
23
|
+
|
24
|
+
## todo/fix: enforce args.size and types.size must match!!
|
25
|
+
## raise ArgumentError - expected x arguments got y!!!
|
26
|
+
|
27
|
+
|
28
|
+
## for convenience check if types is a String
|
29
|
+
## otherwise assume ABI::Type already
|
30
|
+
types = types.map { |type| type.is_a?( Type ) ? type : Type.parse( type ) }
|
31
|
+
|
32
|
+
## todo/check: use args.map (instead of types)
|
33
|
+
## might allow encoding less args than types? - why? why not?
|
34
|
+
head_size = types
|
35
|
+
.map {|type| type.size || 32 }
|
36
|
+
.sum
|
37
|
+
|
38
|
+
head, tail = '', ''
|
39
|
+
types.each_with_index do |type, i|
|
40
|
+
if type.dynamic?
|
41
|
+
head += encode_uint256( head_size + tail.size )
|
42
|
+
tail += encode_type( type, args[i] )
|
43
|
+
else
|
44
|
+
head += encode_type( type, args[i] )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
head + tail
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Encodes a single value (static or dynamic).
|
53
|
+
#
|
54
|
+
# @param type [ABI::Type] value type
|
55
|
+
# @param arg [Object] value
|
56
|
+
#
|
57
|
+
# @return [String] encoded bytes
|
58
|
+
#
|
59
|
+
def encode_type( type, arg )
|
60
|
+
## case 1) string or bytes (note:are dynamic too!!! most go first)
|
61
|
+
## use type == Type.new( 'string', nil, [] ) - same as Type.new('string'))
|
62
|
+
## or type == Type.new( 'bytes', nil, [] ) - same as Type.new('bytes')
|
63
|
+
## - why? why not?
|
64
|
+
if type.is_a?( String )
|
65
|
+
encode_string( arg )
|
66
|
+
elsif type.is_a?( Bytes )
|
67
|
+
encode_bytes( arg )
|
68
|
+
elsif type.is_a?( Tuple )
|
69
|
+
encode_tuple( type, arg )
|
70
|
+
elsif type.is_a?( Array ) || type.is_a?( FixedArray )
|
71
|
+
if type.dynamic?
|
72
|
+
encode_dynamic_array( type, arg )
|
73
|
+
else
|
74
|
+
encode_static_array( type, arg )
|
75
|
+
end
|
76
|
+
else # assume static (primitive) type
|
77
|
+
encode_primitive_type( type, arg )
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def encode_dynamic_array( type, args )
|
83
|
+
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
|
84
|
+
|
85
|
+
head, tail = '', ''
|
86
|
+
|
87
|
+
if type.is_a?( Array ) ## dynamic array
|
88
|
+
head += encode_uint256( args.size )
|
89
|
+
else ## fixed array
|
90
|
+
raise ArgumentError, "Wrong array size: found #{args.size}, expecting #{type.dim}" unless args.size == type.dim
|
91
|
+
end
|
92
|
+
|
93
|
+
subtype = type.subtype
|
94
|
+
args.each do |arg|
|
95
|
+
if subtype.dynamic?
|
96
|
+
head += encode_uint256( 32*args.size + tail.size )
|
97
|
+
tail += encode_type( subtype, arg )
|
98
|
+
else
|
99
|
+
head += encode_type( subtype, arg )
|
100
|
+
end
|
101
|
+
end
|
102
|
+
head + tail
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def encode_static_array( type, args )
|
107
|
+
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
|
108
|
+
raise ArgumentError, "Wrong array size: found #{args.size}, expecting #{type.dim}" unless args.size == type.dim
|
109
|
+
|
110
|
+
args.map {|arg| encode_type( type.subtype, arg ) }.join
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
##
|
115
|
+
## todo/check: if static tuple gets encoded different
|
116
|
+
## without offset (head/tail)
|
117
|
+
|
118
|
+
def encode_tuple( tuple, args )
|
119
|
+
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
|
120
|
+
raise ArgumentError, "Wrong array size (for tuple): found #{args.size}, expecting #{tuple.type.size} tuple elements" unless args.size == tuple.types.size
|
121
|
+
|
122
|
+
head_size = tuple.types
|
123
|
+
.map {|type| type.size || 32 }
|
124
|
+
.sum
|
125
|
+
|
126
|
+
head, tail = '', ''
|
127
|
+
tuple.types.each_with_index do |type, i|
|
128
|
+
if type.dynamic?
|
129
|
+
head += encode_uint256( head_size + tail.size )
|
130
|
+
tail += encode_type( type, args[i] )
|
131
|
+
else
|
132
|
+
head += encode_type( type, args[i] )
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
head + tail
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
def encode_primitive_type( type, arg )
|
142
|
+
case type
|
143
|
+
when Uint
|
144
|
+
## note: for now size in bits always required
|
145
|
+
encode_uint( arg, type.bits )
|
146
|
+
when Int
|
147
|
+
## note: for now size in bits always required
|
148
|
+
encode_int( arg, type.bits )
|
149
|
+
when Bool
|
150
|
+
encode_bool( arg )
|
151
|
+
when String
|
152
|
+
encode_string( arg )
|
153
|
+
when FixedBytes
|
154
|
+
encode_bytes( arg, type.length )
|
155
|
+
when Bytes
|
156
|
+
encode_bytes( arg )
|
157
|
+
when Address
|
158
|
+
encode_address( arg )
|
159
|
+
else
|
160
|
+
raise EncodingError, "Unhandled type: #{type.class.name} #{type.format}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
def encode_bool( arg )
|
167
|
+
raise ArgumentError, "arg is not bool: #{arg}" unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
|
168
|
+
lpad_int( arg ? 1 : 0 )
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
def encode_uint256( arg ) encode_uint( arg, 256 ); end
|
173
|
+
def encode_uint( arg, bits )
|
174
|
+
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
|
175
|
+
raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**bits
|
176
|
+
lpad_int( arg )
|
177
|
+
end
|
178
|
+
|
179
|
+
def encode_int( arg, bits )
|
180
|
+
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
|
181
|
+
raise ValueOutOfBounds, arg unless arg >= -2**(bits-1) && arg < 2**(bits-1)
|
182
|
+
lpad_int( arg % 2**bits )
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
def encode_string( arg )
|
187
|
+
|
188
|
+
## todo/fix: do NOT auto-unpack hexstring EVER!!!
|
189
|
+
## remove arg.unpack('U*')
|
190
|
+
|
191
|
+
if arg.encoding == Encoding::UTF_8 ## was: name == 'UTF-8'
|
192
|
+
arg = arg.b
|
193
|
+
else
|
194
|
+
begin
|
195
|
+
arg.unpack('U*')
|
196
|
+
rescue ArgumentError
|
197
|
+
raise ValueError, "string must be UTF-8 encoded"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX
|
202
|
+
size = lpad_int( arg.size )
|
203
|
+
value = rpad( arg, ceil32(arg.size) )
|
204
|
+
|
205
|
+
size + value
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
def encode_bytes( arg, length=nil )
|
210
|
+
raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String)
|
211
|
+
arg = arg.b
|
212
|
+
|
213
|
+
if length # fixed length type
|
214
|
+
raise ValueOutOfBounds, "invalid bytes length #{length}" if arg.size > length
|
215
|
+
raise ValueOutOfBounds, "invalid bytes length #{length}" if length < 0 || length > 32
|
216
|
+
rpad( arg )
|
217
|
+
else # variable length type (if length is nil)
|
218
|
+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX
|
219
|
+
size = lpad_int( arg.size )
|
220
|
+
value = rpad( arg, ceil32(arg.size) )
|
221
|
+
size + value
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
def encode_address( arg )
|
227
|
+
if arg.is_a?( Integer )
|
228
|
+
lpad_int( arg )
|
229
|
+
elsif arg.size == 20
|
230
|
+
lpad( arg )
|
231
|
+
elsif arg.size == 40
|
232
|
+
lpad_hex( arg )
|
233
|
+
elsif arg.size == 42 && arg[0,2] == '0x' ## todo/fix: allow 0X too - why? why not?
|
234
|
+
lpad_hex( arg[2..-1] )
|
235
|
+
else
|
236
|
+
raise EncodingError, "Could not parse address: #{arg}"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
###########
|
242
|
+
# encoding helpers / utils
|
243
|
+
|
244
|
+
def rpad( bin, l=32, symbol=BYTE_ZERO ) ## note: same as builtin String#ljust !!!
|
245
|
+
return bin if bin.size >= l
|
246
|
+
bin + symbol * (l - bin.size)
|
247
|
+
end
|
248
|
+
|
249
|
+
def lpad( bin, l=32, symbol=BYTE_ZERO ) ## note: same as builtin String#rjust !!!
|
250
|
+
return bin if bin.size >= l
|
251
|
+
symbol * (l - bin.size) + bin
|
252
|
+
end
|
253
|
+
|
254
|
+
def lpad_int( n, l=32, symbol=BYTE_ZERO )
|
255
|
+
raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
256
|
+
hex = n.to_s(16)
|
257
|
+
hex = "0#{hex}" if hex.size.odd?
|
258
|
+
bin = [hex].pack("H*")
|
259
|
+
|
260
|
+
lpad( bin, l, symbol )
|
261
|
+
end
|
262
|
+
|
263
|
+
def lpad_hex( hex, l=32, symbol=BYTE_ZERO )
|
264
|
+
raise TypeError, "Value must be a string" unless hex.is_a?( ::String )
|
265
|
+
raise TypeError, 'Non-hexadecimal digit found' unless hex =~ /\A[0-9a-fA-F]*\z/
|
266
|
+
bin = [hex].pack("H*")
|
267
|
+
|
268
|
+
lpad( bin, l, symbol )
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
|
273
|
+
def ceil32(x)
|
274
|
+
x % 32 == 0 ? x : (x + 32 - x%32)
|
275
|
+
end
|
276
|
+
|
277
|
+
end # class Encoder
|
278
|
+
end # module ABI
|
279
|
+
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module ABI
|
2
|
+
class Type
|
3
|
+
|
4
|
+
class ParseError < StandardError; end
|
5
|
+
|
6
|
+
|
7
|
+
## nested inside Type - why? why not?
|
8
|
+
class Parser
|
9
|
+
|
10
|
+
TUPLE_TYPE_RX = /^\((.*)\)
|
11
|
+
((\[[0-9]*\])*)
|
12
|
+
/x
|
13
|
+
|
14
|
+
def self.parse( type )
|
15
|
+
if type =~ TUPLE_TYPE_RX
|
16
|
+
types = _parse_tuple_type( $1 )
|
17
|
+
dims = _parse_dims( $2 )
|
18
|
+
|
19
|
+
parsed_types = types.map{ |t| parse( t ) }
|
20
|
+
|
21
|
+
return _parse_array_type( Tuple.new( parsed_types ), dims )
|
22
|
+
end
|
23
|
+
|
24
|
+
base, sub, dims = _parse_base_type( type )
|
25
|
+
|
26
|
+
_validate_base_type( base, sub )
|
27
|
+
|
28
|
+
subtype = case base
|
29
|
+
when 'string' then String.new
|
30
|
+
when 'bytes' then sub ? FixedBytes.new( sub ) : Bytes.new
|
31
|
+
when 'uint' then Uint.new( sub )
|
32
|
+
when 'int' then Int.new( sub )
|
33
|
+
when 'address' then Address.new
|
34
|
+
when 'bool' then Bool.new
|
35
|
+
else
|
36
|
+
## puts " type: >#{type}<"
|
37
|
+
raise ParseError, "Unrecognized type base: #{base}"
|
38
|
+
end
|
39
|
+
|
40
|
+
_parse_array_type( subtype, dims )
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Crazy regexp to seperate out base type component (eg. uint), size (eg.
|
45
|
+
# 256, 128, nil), array component (eg. [], [45], nil)
|
46
|
+
#
|
47
|
+
BASE_TYPE_RX = /([a-z]*)
|
48
|
+
([0-9]*)
|
49
|
+
((\[[0-9]*\])*)
|
50
|
+
/x
|
51
|
+
|
52
|
+
def self._parse_base_type( str )
|
53
|
+
_, base, subscript, dimension = BASE_TYPE_RX.match( str ).to_a
|
54
|
+
|
55
|
+
## note: use [Integer,Integer] array in the future for sub
|
56
|
+
## for fixed (e.g. 128x128 => [128,128]) or such
|
57
|
+
## for now always assume single integer (as string)
|
58
|
+
sub = subscript == '' ? nil : subscript.to_i
|
59
|
+
|
60
|
+
## e.g. turn "[][1][2]" into [-1,1,2]
|
61
|
+
## or "" into [] -- that is, empty array
|
62
|
+
dims = _parse_dims( dimension )
|
63
|
+
|
64
|
+
[base, sub, dims]
|
65
|
+
end
|
66
|
+
|
67
|
+
def self._parse_dims( str )
|
68
|
+
dims = str.scan( /\[[0-9]*\]/ )
|
69
|
+
|
70
|
+
## note: return -1 for dynamic array size e.g. []
|
71
|
+
## e.g. "[]"[1...-1] => ""
|
72
|
+
## "[0]"[1...-1] => "0"
|
73
|
+
## "[1]"[1...-1] => "1"
|
74
|
+
dims = dims.map do |dim|
|
75
|
+
size = dim[1...-1]
|
76
|
+
size == '' ? -1 : size.to_i
|
77
|
+
end
|
78
|
+
dims
|
79
|
+
end
|
80
|
+
|
81
|
+
def self._parse_array_type( subtype, dims )
|
82
|
+
##
|
83
|
+
## todo/check - double check if the order in reverse
|
84
|
+
## in solidity / abi encoding / decoding?
|
85
|
+
##
|
86
|
+
dims.each do |dim|
|
87
|
+
subtype = if dim == -1
|
88
|
+
Array.new( subtype )
|
89
|
+
else
|
90
|
+
FixedArray.new( subtype, dim )
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
subtype
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def self._validate_base_type( base, sub )
|
99
|
+
case base
|
100
|
+
when 'string'
|
101
|
+
# note: string can not have any suffix
|
102
|
+
raise ParseError, "String cannot have suffix" if sub
|
103
|
+
when 'bytes'
|
104
|
+
raise ParseError, "Maximum 32 bytes for fixed-length bytes" if sub && sub > 32
|
105
|
+
when 'uint', 'int'
|
106
|
+
raise ParseError, "Integer type must have numerical suffix" unless sub
|
107
|
+
raise ParseError, "Integer size out of bounds" unless sub >= 8 && sub <= 256
|
108
|
+
raise ParseError, "Integer size must be multiple of 8" unless sub % 8 == 0
|
109
|
+
when 'address'
|
110
|
+
raise ParseError, "Address cannot have suffix" if sub
|
111
|
+
when 'bool'
|
112
|
+
raise ParseError, "Bool cannot have suffix" if sub
|
113
|
+
else
|
114
|
+
## puts " type: >#{type}<"
|
115
|
+
raise ParseError, "Unrecognized type base: #{base}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
def self._parse_tuple_type( str )
|
122
|
+
## note: types assumes string WITHOUT enclosing () e.g.
|
123
|
+
## tuple(string,string,bool) => expected as "string,string,bool"
|
124
|
+
|
125
|
+
depth = 0
|
126
|
+
collected = []
|
127
|
+
current = ''
|
128
|
+
|
129
|
+
### todo/fix: replace with a simple parser!!!
|
130
|
+
## allow () and move verbose tuple() too!!!
|
131
|
+
str.each_char do |c|
|
132
|
+
case c
|
133
|
+
when ',' then
|
134
|
+
if depth == 0
|
135
|
+
collected << current
|
136
|
+
current = ''
|
137
|
+
else
|
138
|
+
current += c
|
139
|
+
end
|
140
|
+
when '(' then
|
141
|
+
depth += 1
|
142
|
+
current += c
|
143
|
+
when ')' then
|
144
|
+
depth -= 1
|
145
|
+
current += c
|
146
|
+
else
|
147
|
+
current += c
|
148
|
+
end
|
149
|
+
end
|
150
|
+
collected << current unless current.empty?
|
151
|
+
|
152
|
+
collected
|
153
|
+
end
|
154
|
+
|
155
|
+
end # class Parser
|
156
|
+
end # class Type
|
157
|
+
end # module ABI
|
158
|
+
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module ABI
|
2
|
+
|
3
|
+
|
4
|
+
#######
|
5
|
+
## for now use (get inspired)
|
6
|
+
## by the type names used by abi coder in rust
|
7
|
+
## see https://github.com/rust-ethereum/ethabi/blob/master/ethabi/src/param_type/param_type.rs
|
8
|
+
|
9
|
+
|
10
|
+
class Type
|
11
|
+
|
12
|
+
def self.parse( type ) ## convenience helper
|
13
|
+
Parser.parse( type )
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Get the static size of a type, or nil if dynamic.
|
18
|
+
#
|
19
|
+
# @return [Integer, NilClass] size of static type, or nil for dynamic type
|
20
|
+
#
|
21
|
+
def size
|
22
|
+
## check/todo: what error to raise for not implemented / method not defined???
|
23
|
+
raise ArgumentError, "no required size method defined for Type subclass #{self.class.name}; sorry"
|
24
|
+
end
|
25
|
+
def dynamic?() size.nil?; end
|
26
|
+
|
27
|
+
def format
|
28
|
+
## check/todo: what error to raise for not implemented / method not defined???
|
29
|
+
raise ArgumentError, "no required format method defined for Type subclass #{self.class.name}; sorry"
|
30
|
+
end
|
31
|
+
|
32
|
+
####
|
33
|
+
## default implementation
|
34
|
+
## assume equal if class match (e.g. Address == Address)
|
35
|
+
## - use format string for generic compare - why? why not?
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
class Address < Type
|
41
|
+
def size() 32; end ## note: address is always 20 bytes; BUT uses 32 bytes (with padding)
|
42
|
+
def format() 'address'; end
|
43
|
+
def ==(another_type) another_type.kind_of?( Address ); end
|
44
|
+
end # class Address
|
45
|
+
|
46
|
+
class Bytes < Type
|
47
|
+
def size() nil; end ## note: dynamic (not known at compile-time)
|
48
|
+
def format() 'bytes'; end
|
49
|
+
def ==(another_type) another_type.kind_of?( Bytes ); end
|
50
|
+
end # class Bytes
|
51
|
+
|
52
|
+
class FixedBytes < Type
|
53
|
+
attr_reader :length
|
54
|
+
def initialize( length )
|
55
|
+
@length = length # in bytes (1,2,...32)
|
56
|
+
end
|
57
|
+
def size() 32; end ## note: always uses 32 bytes (with padding)
|
58
|
+
def format() "bytes#{@length}"; end
|
59
|
+
def ==(another_type)
|
60
|
+
another_type.kind_of?( FixedBytes ) && @length == another_type.length
|
61
|
+
end
|
62
|
+
end # class FixedBytes
|
63
|
+
|
64
|
+
class Int < Type
|
65
|
+
attr_reader :bits
|
66
|
+
def initialize( bits=256 )
|
67
|
+
@bits = bits # in bits (8,16,...256)
|
68
|
+
end
|
69
|
+
def size() 32; end ## note: always uses 32 bytes (with padding)
|
70
|
+
def format() "int#{@bits}"; end
|
71
|
+
def ==(another_type)
|
72
|
+
another_type.kind_of?( Int ) && @bits == another_type.bits
|
73
|
+
end
|
74
|
+
end # class Int
|
75
|
+
|
76
|
+
class Uint < Type
|
77
|
+
attr_reader :bits
|
78
|
+
def initialize( bits=256 )
|
79
|
+
@bits = bits # in bits (8,16,...256)
|
80
|
+
end
|
81
|
+
def size() 32; end ## note: always uses 32 bytes (with padding)
|
82
|
+
def format() "uint#{@bits}"; end
|
83
|
+
def ==(another_type)
|
84
|
+
another_type.kind_of?( Uint ) && @bits == another_type.bits
|
85
|
+
end
|
86
|
+
end # class Uint
|
87
|
+
|
88
|
+
class Bool < Type
|
89
|
+
def size() 32; end ## note: always uses 32 bytes (with padding)
|
90
|
+
def format() 'bool'; end
|
91
|
+
def ==(another_type) another_type.kind_of?( Bool ); end
|
92
|
+
end # class Bool
|
93
|
+
|
94
|
+
class String < Type
|
95
|
+
def size() nil; end ## note: dynamic (not known at compile-time)
|
96
|
+
def format() 'string'; end
|
97
|
+
def ==(another_type) another_type.kind_of?( String ); end
|
98
|
+
end # class String
|
99
|
+
|
100
|
+
class Array < Type
|
101
|
+
attr_reader :subtype
|
102
|
+
def initialize( subtype )
|
103
|
+
@subtype = subtype
|
104
|
+
end
|
105
|
+
def size() nil; end ## note: dynamic (not known at compile-time)
|
106
|
+
def format() "#{@subtype.format}[]"; end
|
107
|
+
def ==(another_type)
|
108
|
+
another_type.kind_of?( Array ) && @subtype == another_type.subtype
|
109
|
+
end
|
110
|
+
end # class Array
|
111
|
+
|
112
|
+
class FixedArray < Type
|
113
|
+
attr_reader :subtype
|
114
|
+
attr_reader :dim
|
115
|
+
def initialize( subtype, dim )
|
116
|
+
@subtype = subtype
|
117
|
+
@dim = dim
|
118
|
+
end
|
119
|
+
|
120
|
+
def size
|
121
|
+
@subtype.dynamic? ? nil : @dim * subtype.size
|
122
|
+
end
|
123
|
+
def format() "#{@subtype.format}[#{@dim}]"; end
|
124
|
+
def ==(another_type)
|
125
|
+
another_type.kind_of?( FixedArray ) &&
|
126
|
+
@dim == another_type.dim &&
|
127
|
+
@subtype == another_type.subtype
|
128
|
+
end
|
129
|
+
end # class FixedArray
|
130
|
+
|
131
|
+
class Tuple < Type
|
132
|
+
attr_reader :types
|
133
|
+
|
134
|
+
def initialize( types )
|
135
|
+
@types = types
|
136
|
+
end
|
137
|
+
|
138
|
+
def size
|
139
|
+
s = 0
|
140
|
+
@types.each do |type|
|
141
|
+
ts = type.size
|
142
|
+
return nil if ts.nil?
|
143
|
+
s += ts
|
144
|
+
end
|
145
|
+
s
|
146
|
+
end
|
147
|
+
def format
|
148
|
+
"(#{@types.map {|t| t.format }.join(',')})" ## rebuild minimal string
|
149
|
+
end
|
150
|
+
def ==(another_type)
|
151
|
+
another_type.kind_of?( Tuple ) && @types == another_type.types
|
152
|
+
end
|
153
|
+
end # class Tuple
|
154
|
+
end # module ABI
|
155
|
+
|
data/lib/abicoder/version.rb
CHANGED
data/lib/abicoder.rb
CHANGED
@@ -4,13 +4,65 @@
|
|
4
4
|
require_relative 'abicoder/version' # note: let version always go first
|
5
5
|
|
6
6
|
|
7
|
-
|
8
7
|
module ABI
|
8
|
+
###################
|
9
|
+
### some (shared) constants (move to constants.rb or such - why? why not?)
|
10
|
+
|
11
|
+
## todo/check: use auto-freeze string literals magic comment - why? why not?
|
12
|
+
##
|
13
|
+
## todo/fix: move BYTE_EMPTY, BYTE_ZERO, BYTE_ONE to upstream to bytes gem
|
14
|
+
## and make "global" constants - why? why not?
|
15
|
+
|
16
|
+
BYTE_EMPTY = "".b.freeze
|
17
|
+
BYTE_ZERO = "\x00".b.freeze
|
18
|
+
BYTE_ONE = "\x01".b.freeze ## note: used for encoding bool for now
|
19
|
+
|
9
20
|
|
21
|
+
UINT_MAX = 2**256 - 1 ## same as 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
22
|
+
UINT_MIN = 0
|
23
|
+
INT_MAX = 2**255 - 1 ## same as 57896044618658097711785492504343953926634992332820282019728792003956564819967
|
24
|
+
INT_MIN = -2**255 ## same as -57896044618658097711785492504343953926634992332820282019728792003956564819968
|
10
25
|
|
11
26
|
end # module ABI
|
12
27
|
|
13
28
|
|
29
|
+
require_relative 'abicoder/types'
|
30
|
+
require_relative 'abicoder/parser'
|
31
|
+
|
32
|
+
|
33
|
+
require_relative 'abicoder/encoder'
|
34
|
+
require_relative 'abicoder/decoder'
|
35
|
+
|
36
|
+
|
37
|
+
module ABI
|
38
|
+
def self.encoder
|
39
|
+
@encoder ||= Encoder.new
|
40
|
+
end
|
41
|
+
def self.decoder
|
42
|
+
@decoder ||= Decoder.new
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def self.encode( types, args )
|
47
|
+
encoder.encode( types, args )
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.decode( types, data, raise_errors = false )
|
51
|
+
decoder.decode( types, data, raise_errors )
|
52
|
+
end
|
53
|
+
|
54
|
+
## add alternate _abi names - why? why not?
|
55
|
+
class << self
|
56
|
+
alias_method :encode_abi, :encode
|
57
|
+
alias_method :decode_abi, :decode
|
58
|
+
end
|
59
|
+
end ## module ABI
|
60
|
+
|
61
|
+
|
62
|
+
################
|
63
|
+
## add convenience alternate spellings - why? why not?
|
64
|
+
Abi = ABI
|
65
|
+
|
14
66
|
|
15
67
|
|
16
68
|
puts ABICoder.banner ## say hello
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abicoder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdoc
|
@@ -44,8 +44,9 @@ dependencies:
|
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '3.23'
|
47
|
-
description: abicoder - application binary interface (abi) encoding / decoding
|
48
|
-
/ helper for Ethereum & Co. (blockchain) contracts
|
47
|
+
description: abicoder - 'lite' application binary interface (abi) encoding / decoding
|
48
|
+
machinery / helper for Ethereum & Co. (blockchain) contracts with zero-dependencies
|
49
|
+
for easy (re)use
|
49
50
|
email: wwwmake@googlegroups.com
|
50
51
|
executables: []
|
51
52
|
extensions: []
|
@@ -59,6 +60,10 @@ files:
|
|
59
60
|
- README.md
|
60
61
|
- Rakefile
|
61
62
|
- lib/abicoder.rb
|
63
|
+
- lib/abicoder/decoder.rb
|
64
|
+
- lib/abicoder/encoder.rb
|
65
|
+
- lib/abicoder/parser.rb
|
66
|
+
- lib/abicoder/types.rb
|
62
67
|
- lib/abicoder/version.rb
|
63
68
|
homepage: https://github.com/rubycocos/blockchain
|
64
69
|
licenses:
|
@@ -84,6 +89,7 @@ requirements: []
|
|
84
89
|
rubygems_version: 3.3.7
|
85
90
|
signing_key:
|
86
91
|
specification_version: 4
|
87
|
-
summary: abicoder - application binary interface (abi) encoding / decoding
|
88
|
-
/ helper for Ethereum & Co. (blockchain) contracts
|
92
|
+
summary: abicoder - 'lite' application binary interface (abi) encoding / decoding
|
93
|
+
machinery / helper for Ethereum & Co. (blockchain) contracts with zero-dependencies
|
94
|
+
for easy (re)use
|
89
95
|
test_files: []
|