| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Web3::Tiny::ABI; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
101725
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
82
|
|
|
4
|
2
|
|
|
2
|
|
11
|
use warnings; |
|
|
2
|
|
|
|
|
2
|
|
|
|
2
|
|
|
|
|
82
|
|
|
5
|
2
|
|
|
2
|
|
8
|
use Exporter 'import'; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
55
|
|
|
6
|
2
|
|
|
2
|
|
1468
|
use Math::BigInt; |
|
|
2
|
|
|
|
|
38138
|
|
|
|
2
|
|
|
|
|
18
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
2
|
|
|
2
|
|
29705
|
use Web3::Tiny::Keccak256 qw(keccak256); |
|
|
2
|
|
|
|
|
12
|
|
|
|
2
|
|
|
|
|
4922
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
|
11
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
|
12
|
|
|
|
|
|
|
selector encode_call encode_types decode_result decode_types |
|
13
|
|
|
|
|
|
|
); |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 NAME |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Web3::Tiny::ABI - Solidity ABI encoding/decoding |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
Supports the common scalar types (C, C, C, C, |
|
22
|
|
|
|
|
|
|
C, dynamic C/C) plus C and C arrays of |
|
23
|
|
|
|
|
|
|
them. Does B support tuples/structs or arrays-of-arrays -- if you |
|
24
|
|
|
|
|
|
|
need those, this is intentionally the "tiny" subset. |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Integers are accepted as plain Perl numbers, numeric strings, or |
|
27
|
|
|
|
|
|
|
L objects, and are returned from C as |
|
28
|
|
|
|
|
|
|
Math::BigInt objects (uint256 routinely exceeds native int range). |
|
29
|
|
|
|
|
|
|
C values are accepted/returned as C<"0x...">-prefixed 40-hex-char |
|
30
|
|
|
|
|
|
|
strings. C/C values are accepted/returned as raw byte |
|
31
|
|
|
|
|
|
|
strings unless given as a C<"0x...">-prefixed hex string, in which case |
|
32
|
|
|
|
|
|
|
that convention is preserved on decode too... actually bytes/bytesN are |
|
33
|
|
|
|
|
|
|
always *returned* as raw bytes; only C round-trips as hex text. |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=cut |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# ------------------------------------------------------------------ |
|
38
|
|
|
|
|
|
|
# type introspection |
|
39
|
|
|
|
|
|
|
# ------------------------------------------------------------------ |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
sub _is_dynamic_type { |
|
42
|
34
|
|
|
34
|
|
62
|
my ($type) = @_; |
|
43
|
34
|
100
|
66
|
|
|
140
|
return 1 if $type eq 'bytes' || $type eq 'string'; |
|
44
|
30
|
100
|
|
|
|
99
|
if ($type =~ /^(.+)\[(\d*)\]$/) { |
|
45
|
4
|
|
|
|
|
22
|
my ($elem, $size) = ($1, $2); |
|
46
|
4
|
50
|
|
|
|
20
|
return 1 if $size eq ''; |
|
47
|
0
|
|
|
|
|
0
|
return _is_dynamic_type($elem); |
|
48
|
|
|
|
|
|
|
} |
|
49
|
26
|
|
|
|
|
63
|
return 0; |
|
50
|
|
|
|
|
|
|
} |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub _static_size { |
|
53
|
13
|
|
|
13
|
|
24
|
my ($type) = @_; |
|
54
|
13
|
50
|
|
|
|
29
|
if ($type =~ /^(.+)\[(\d+)\]$/) { |
|
55
|
0
|
|
|
|
|
0
|
return _static_size($1) * $2; |
|
56
|
|
|
|
|
|
|
} |
|
57
|
13
|
|
|
|
|
35
|
return 32; |
|
58
|
|
|
|
|
|
|
} |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# ------------------------------------------------------------------ |
|
61
|
|
|
|
|
|
|
# scalar codecs |
|
62
|
|
|
|
|
|
|
# ------------------------------------------------------------------ |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub _to_bigint { |
|
65
|
8
|
|
|
8
|
|
15
|
my ($n) = @_; |
|
66
|
8
|
50
|
33
|
|
|
19
|
return $n->copy if ref($n) && $n->isa('Math::BigInt'); |
|
67
|
8
|
|
|
|
|
46
|
return Math::BigInt->new("$n"); |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub _encode_uint_word { |
|
71
|
8
|
|
|
8
|
|
40
|
my ($n) = @_; |
|
72
|
8
|
|
|
|
|
64
|
my $bi = _to_bigint($n); |
|
73
|
8
|
50
|
|
|
|
885
|
die "Web3::Tiny::ABI: negative value not valid for unsigned type\n" if $bi->is_neg; |
|
74
|
8
|
|
|
|
|
71
|
my $hex = $bi->as_hex; |
|
75
|
8
|
|
|
|
|
759
|
$hex =~ s/^0x//; |
|
76
|
8
|
50
|
|
|
|
19
|
die "Web3::Tiny::ABI: integer too large for uint256\n" if length($hex) > 64; |
|
77
|
8
|
|
|
|
|
27
|
$hex = ('0' x (64 - length($hex))) . $hex; |
|
78
|
8
|
|
|
|
|
97
|
return pack('H*', $hex); |
|
79
|
|
|
|
|
|
|
} |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub _decode_uint_word { |
|
82
|
7
|
|
|
7
|
|
16
|
my ($word) = @_; |
|
83
|
7
|
|
|
|
|
42
|
return Math::BigInt->from_hex('0x' . unpack('H*', $word)); |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub _encode_int_word { |
|
87
|
0
|
|
|
0
|
|
0
|
my ($n) = @_; |
|
88
|
0
|
|
|
|
|
0
|
my $bi = _to_bigint($n); |
|
89
|
0
|
0
|
|
|
|
0
|
if ($bi->is_neg) { |
|
90
|
0
|
|
|
|
|
0
|
my $two_256 = Math::BigInt->new(2)->bpow(256); |
|
91
|
0
|
|
|
|
|
0
|
$bi = $two_256->badd($bi); |
|
92
|
|
|
|
|
|
|
} |
|
93
|
0
|
|
|
|
|
0
|
return _encode_uint_word($bi); |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub _decode_int_word { |
|
97
|
0
|
|
|
0
|
|
0
|
my ($word) = @_; |
|
98
|
0
|
|
|
|
|
0
|
my $v = _decode_uint_word($word); |
|
99
|
0
|
|
|
|
|
0
|
my $two_255 = Math::BigInt->new(2)->bpow(255); |
|
100
|
0
|
0
|
|
|
|
0
|
if ($v->bcmp($two_255) >= 0) { |
|
101
|
0
|
|
|
|
|
0
|
my $two_256 = Math::BigInt->new(2)->bpow(256); |
|
102
|
0
|
|
|
|
|
0
|
$v = $v->bsub($two_256); |
|
103
|
|
|
|
|
|
|
} |
|
104
|
0
|
|
|
|
|
0
|
return $v; |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub _hexish_to_bytes { |
|
108
|
2
|
|
|
2
|
|
5
|
my ($val) = @_; |
|
109
|
2
|
50
|
|
|
|
7
|
return '' unless defined $val; |
|
110
|
2
|
100
|
|
|
|
9
|
if ($val =~ /^0x([0-9a-fA-F]*)$/) { |
|
111
|
1
|
|
|
|
|
3
|
my $hex = $1; |
|
112
|
1
|
50
|
|
|
|
5
|
$hex = '0' . $hex if length($hex) % 2; |
|
113
|
1
|
|
|
|
|
8
|
return pack('H*', $hex); |
|
114
|
|
|
|
|
|
|
} |
|
115
|
1
|
|
|
|
|
3
|
return $val; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub _encode_address { |
|
119
|
1
|
|
|
1
|
|
3
|
my ($addr) = @_; |
|
120
|
1
|
|
|
|
|
4
|
my $bytes = _hexish_to_bytes($addr); |
|
121
|
1
|
50
|
|
|
|
3
|
die "Web3::Tiny::ABI: address must be 20 bytes\n" unless length($bytes) == 20; |
|
122
|
1
|
|
|
|
|
6
|
return ("\x00" x 12) . $bytes; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub _decode_address { |
|
126
|
1
|
|
|
1
|
|
4
|
my ($word) = @_; |
|
127
|
1
|
|
|
|
|
8
|
return '0x' . unpack('H*', substr($word, 12, 20)); |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub _encode_bytesN { |
|
131
|
1
|
|
|
1
|
|
5
|
my ($val, $n) = @_; |
|
132
|
1
|
|
|
|
|
4
|
my $bytes = _hexish_to_bytes($val); |
|
133
|
1
|
50
|
|
|
|
5
|
die "Web3::Tiny::ABI: bytes$n value must be exactly $n bytes\n" unless length($bytes) == $n; |
|
134
|
1
|
|
|
|
|
6
|
return $bytes . ("\x00" x (32 - $n)); |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
sub _encode_bytes_dynamic { |
|
138
|
1
|
|
|
1
|
|
2
|
my ($bytes) = @_; |
|
139
|
1
|
|
|
|
|
19
|
my $len = length($bytes); |
|
140
|
1
|
|
|
|
|
3
|
my $pad = (32 - ($len % 32)) % 32; |
|
141
|
1
|
|
|
|
|
24
|
return _encode_uint_word($len) . $bytes . ("\x00" x $pad); |
|
142
|
|
|
|
|
|
|
} |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# ------------------------------------------------------------------ |
|
145
|
|
|
|
|
|
|
# generic tuple (and by extension: array) encode/decode |
|
146
|
|
|
|
|
|
|
# |
|
147
|
|
|
|
|
|
|
# Offsets for dynamic values are always relative to the start of the |
|
148
|
|
|
|
|
|
|
# *current* tuple's own data window -- whether that's the top-level |
|
149
|
|
|
|
|
|
|
# call arguments, or an array's element region right after its length |
|
150
|
|
|
|
|
|
|
# word. This keeps encode/decode of nested dynamic arrays consistent. |
|
151
|
|
|
|
|
|
|
# ------------------------------------------------------------------ |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub _slot_sizes { |
|
154
|
8
|
|
|
8
|
|
18
|
my ($types) = @_; |
|
155
|
8
|
100
|
|
|
|
20
|
return [map { _is_dynamic_type($_) ? 32 : _static_size($_) } @$types]; |
|
|
17
|
|
|
|
|
32
|
|
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub encode_types { |
|
159
|
3
|
|
|
3
|
0
|
905
|
my ($types, $values) = @_; |
|
160
|
3
|
50
|
|
|
|
9
|
die "Web3::Tiny::ABI: types/values count mismatch\n" unless @$types == @$values; |
|
161
|
|
|
|
|
|
|
|
|
162
|
3
|
|
|
|
|
9
|
my $slot_sizes = _slot_sizes($types); |
|
163
|
3
|
|
|
|
|
5
|
my $head_size = 0; |
|
164
|
3
|
|
|
|
|
10
|
$head_size += $_ for @$slot_sizes; |
|
165
|
|
|
|
|
|
|
|
|
166
|
3
|
|
|
|
|
6
|
my @head_parts; |
|
167
|
|
|
|
|
|
|
my @tail_parts; |
|
168
|
3
|
|
|
|
|
11
|
for my $i (0 .. $#$types) { |
|
169
|
8
|
|
|
|
|
17
|
my $type = $types->[$i]; |
|
170
|
8
|
|
|
|
|
11
|
my $val = $values->[$i]; |
|
171
|
8
|
100
|
|
|
|
15
|
if (_is_dynamic_type($type)) { |
|
172
|
2
|
|
|
|
|
9
|
push @head_parts, { dyn => 1, idx => scalar(@tail_parts) }; |
|
173
|
2
|
|
|
|
|
6
|
push @tail_parts, _encode_value($type, $val); |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
else { |
|
176
|
6
|
|
|
|
|
16
|
push @head_parts, { raw => _encode_value($type, $val) }; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
|
|
180
|
3
|
|
|
|
|
8
|
my @tail_offsets; |
|
181
|
3
|
|
|
|
|
4
|
my $running = 0; |
|
182
|
3
|
|
|
|
|
6
|
for my $tp (@tail_parts) { |
|
183
|
2
|
|
|
|
|
5
|
push @tail_offsets, $running; |
|
184
|
2
|
|
|
|
|
4
|
$running += length($tp); |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
|
|
187
|
3
|
|
|
|
|
7
|
my $head_str = ''; |
|
188
|
3
|
|
|
|
|
4
|
for my $hp (@head_parts) { |
|
189
|
8
|
100
|
|
|
|
21
|
if ($hp->{raw}) { |
|
190
|
6
|
|
|
|
|
13
|
$head_str .= $hp->{raw}; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
else { |
|
193
|
2
|
|
|
|
|
7
|
$head_str .= _encode_uint_word($head_size + $tail_offsets[$hp->{idx}]); |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
3
|
|
|
|
|
30
|
return $head_str . join('', @tail_parts); |
|
198
|
|
|
|
|
|
|
} |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub _encode_value { |
|
201
|
8
|
|
|
8
|
|
18
|
my ($type, $value) = @_; |
|
202
|
|
|
|
|
|
|
|
|
203
|
8
|
100
|
|
|
|
20
|
if ($type =~ /^(.+)\[(\d*)\]$/) { |
|
204
|
1
|
|
|
|
|
4
|
my ($elem, $size) = ($1, $2); |
|
205
|
1
|
50
|
|
|
|
6
|
my @vals = ref($value) eq 'ARRAY' ? @$value : ($value); |
|
206
|
1
|
50
|
|
|
|
4
|
if ($size eq '') { |
|
207
|
1
|
|
|
|
|
3
|
return _encode_uint_word(scalar @vals) |
|
208
|
|
|
|
|
|
|
. encode_types([($elem) x scalar(@vals)], \@vals); |
|
209
|
|
|
|
|
|
|
} |
|
210
|
0
|
0
|
|
|
|
0
|
die "Web3::Tiny::ABI: expected $size elements for $type, got " . scalar(@vals) . "\n" |
|
211
|
|
|
|
|
|
|
unless scalar(@vals) == $size; |
|
212
|
0
|
|
|
|
|
0
|
return encode_types([($elem) x $size], \@vals); |
|
213
|
|
|
|
|
|
|
} |
|
214
|
|
|
|
|
|
|
|
|
215
|
7
|
100
|
|
|
|
19
|
return _encode_bytes_dynamic($value) if $type eq 'bytes'; |
|
216
|
6
|
50
|
|
|
|
13
|
return _encode_bytes_dynamic(_utf8_bytes($value)) if $type eq 'string'; |
|
217
|
6
|
100
|
|
|
|
17
|
return _encode_address($value) if $type eq 'address'; |
|
218
|
5
|
0
|
|
|
|
9
|
return _encode_uint_word($value ? 1 : 0) if $type eq 'bool'; |
|
|
|
50
|
|
|
|
|
|
|
219
|
5
|
100
|
|
|
|
24
|
return _encode_uint_word($value) if $type =~ /^uint\d*$/; |
|
220
|
1
|
50
|
|
|
|
5
|
return _encode_int_word($value) if $type =~ /^int\d*$/; |
|
221
|
1
|
50
|
|
|
|
9
|
return _encode_bytesN($value, $1) if $type =~ /^bytes(\d+)$/; |
|
222
|
0
|
|
|
|
|
0
|
die "Web3::Tiny::ABI: unsupported type '$type'\n"; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub _utf8_bytes { |
|
226
|
0
|
|
|
0
|
|
0
|
my ($str) = @_; |
|
227
|
0
|
0
|
|
|
|
0
|
return '' unless defined $str; |
|
228
|
0
|
0
|
|
|
|
0
|
return $str unless utf8::is_utf8($str); |
|
229
|
0
|
|
|
|
|
0
|
require Encode; |
|
230
|
0
|
|
|
|
|
0
|
return Encode::encode('UTF-8', $str); |
|
231
|
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub decode_types { |
|
234
|
5
|
|
|
5
|
0
|
908
|
my ($types, $data) = @_; |
|
235
|
5
|
|
|
|
|
12
|
my $slot_sizes = _slot_sizes($types); |
|
236
|
5
|
|
|
|
|
10
|
my @slot_offsets; |
|
237
|
5
|
|
|
|
|
9
|
my $running = 0; |
|
238
|
5
|
|
|
|
|
10
|
for my $sz (@$slot_sizes) { |
|
239
|
9
|
|
|
|
|
16
|
push @slot_offsets, $running; |
|
240
|
9
|
|
|
|
|
16
|
$running += $sz; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
5
|
|
|
|
|
10
|
my @out; |
|
244
|
5
|
|
|
|
|
16
|
for my $i (0 .. $#$types) { |
|
245
|
9
|
|
|
|
|
997
|
push @out, _decode_value($types->[$i], $data, $slot_offsets[$i]); |
|
246
|
|
|
|
|
|
|
} |
|
247
|
5
|
|
|
|
|
313
|
return @out; |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
sub _decode_value { |
|
251
|
9
|
|
|
9
|
|
23
|
my ($type, $data, $head_offset) = @_; |
|
252
|
|
|
|
|
|
|
|
|
253
|
9
|
100
|
|
|
|
18
|
if (_is_dynamic_type($type)) { |
|
254
|
2
|
|
|
|
|
9
|
my $rel = _decode_uint_word(substr($data, $head_offset, 32))->numify; |
|
255
|
2
|
|
|
|
|
1089
|
return _decode_dynamic($type, substr($data, $rel)); |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
7
|
|
|
|
|
19
|
my $word = substr($data, $head_offset, 32); |
|
259
|
7
|
|
|
|
|
18
|
return _decode_static($type, $word, $data, $head_offset); |
|
260
|
|
|
|
|
|
|
} |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# static value living inline in the head (word already sliced out, but |
|
263
|
|
|
|
|
|
|
# fixed-size arrays of static elements need the wider window too) |
|
264
|
|
|
|
|
|
|
sub _decode_static { |
|
265
|
7
|
|
|
7
|
|
18
|
my ($type, $word, $data, $head_offset) = @_; |
|
266
|
|
|
|
|
|
|
|
|
267
|
7
|
50
|
|
|
|
19
|
if ($type =~ /^(.+)\[(\d+)\]$/) { |
|
268
|
0
|
|
|
|
|
0
|
my ($elem, $size) = ($1, $2); |
|
269
|
0
|
|
|
|
|
0
|
my $window = substr($data, $head_offset, 32 * $size); |
|
270
|
0
|
|
|
|
|
0
|
return [decode_types([($elem) x $size], $window)]; |
|
271
|
|
|
|
|
|
|
} |
|
272
|
|
|
|
|
|
|
|
|
273
|
7
|
100
|
|
|
|
26
|
return $word eq "\x00" x 32 ? 0 : 1 if $type eq 'bool'; |
|
|
|
100
|
|
|
|
|
|
|
274
|
5
|
100
|
|
|
|
18
|
return _decode_address($word) if $type eq 'address'; |
|
275
|
4
|
100
|
|
|
|
18
|
return _decode_uint_word($word) if $type =~ /^uint\d*$/; |
|
276
|
1
|
50
|
|
|
|
5
|
return _decode_int_word($word) if $type =~ /^int\d*$/; |
|
277
|
1
|
50
|
|
|
|
10
|
return substr($word, 0, $1) if $type =~ /^bytes(\d+)$/; |
|
278
|
0
|
|
|
|
|
0
|
die "Web3::Tiny::ABI: unsupported type '$type'\n"; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# dynamic value: $data here is already windowed to start at the value's |
|
282
|
|
|
|
|
|
|
# own offset (i.e. $data =~ /^length . content/ for bytes/string/array) |
|
283
|
|
|
|
|
|
|
sub _decode_dynamic { |
|
284
|
2
|
|
|
2
|
|
8
|
my ($type, $data) = @_; |
|
285
|
|
|
|
|
|
|
|
|
286
|
2
|
100
|
|
|
|
13
|
if ($type =~ /^(.+)\[\]$/) { |
|
287
|
1
|
|
|
|
|
3
|
my $elem = $1; |
|
288
|
1
|
|
|
|
|
4
|
my $count = _decode_uint_word(substr($data, 0, 32))->numify; |
|
289
|
1
|
|
|
|
|
298
|
return [decode_types([($elem) x $count], substr($data, 32))]; |
|
290
|
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
|
|
292
|
1
|
|
|
|
|
4
|
my $len = _decode_uint_word(substr($data, 0, 32))->numify; |
|
293
|
1
|
|
|
|
|
289
|
return substr($data, 32, $len); |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# ------------------------------------------------------------------ |
|
297
|
|
|
|
|
|
|
# function-call level helpers |
|
298
|
|
|
|
|
|
|
# ------------------------------------------------------------------ |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
sub _parse_signature { |
|
301
|
1
|
|
|
1
|
|
3
|
my ($sig) = @_; |
|
302
|
1
|
50
|
|
|
|
15
|
die "Web3::Tiny::ABI: bad signature '$sig'\n" |
|
303
|
|
|
|
|
|
|
unless $sig =~ /^\s*(\w+)\s*\(\s*(.*?)\s*\)\s*$/; |
|
304
|
1
|
|
|
|
|
7
|
my ($name, $arglist) = ($1, $2); |
|
305
|
1
|
50
|
|
|
|
11
|
my @types = length($arglist) ? split(/\s*,\s*/, $arglist) : (); |
|
306
|
1
|
|
|
|
|
4
|
return ($name, \@types); |
|
307
|
|
|
|
|
|
|
} |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# selector($sig) -> 4 raw bytes |
|
310
|
|
|
|
|
|
|
sub selector { |
|
311
|
3
|
|
|
3
|
0
|
237422
|
my ($sig) = @_; |
|
312
|
3
|
|
|
|
|
15
|
return substr(keccak256($sig), 0, 4); |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# encode_call($sig, @args) -> raw call data (selector . encoded args) |
|
316
|
|
|
|
|
|
|
sub encode_call { |
|
317
|
1
|
|
|
1
|
0
|
986
|
my ($sig, @args) = @_; |
|
318
|
1
|
|
|
|
|
22
|
my (undef, $types) = _parse_signature($sig); |
|
319
|
1
|
50
|
|
|
|
5
|
die "Web3::Tiny::ABI: '$sig' expects " . scalar(@$types) . " args, got " . scalar(@args) . "\n" |
|
320
|
|
|
|
|
|
|
unless scalar(@$types) == scalar(@args); |
|
321
|
1
|
|
|
|
|
3
|
return selector($sig) . encode_types($types, \@args); |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# decode_result(\@types, $raw_bytes) -> list of decoded values |
|
325
|
|
|
|
|
|
|
sub decode_result { |
|
326
|
3
|
|
|
3
|
0
|
3877
|
my ($types, $data) = @_; |
|
327
|
3
|
|
|
|
|
9
|
return decode_types($types, $data); |
|
328
|
|
|
|
|
|
|
} |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
1; |