File Coverage

blib/lib/Blockchain/Ethereum/ABI/Encoder.pm
Criterion Covered Total %
statement 59 59 100.0
branch 2 4 50.0
condition 1 3 33.3
subroutine 16 16 100.0
pod 5 7 71.4
total 83 89 93.2


line stmt bran cond sub pod time code
1 5     5   422992 use v5.26;
  5         60  
2 5     5   2515 use Object::Pad ':experimental(init_expr)';
  5         45297  
  5         25  
3              
4             package Blockchain::Ethereum::ABI::Encoder 0.012;
5             class Blockchain::Ethereum::ABI::Encoder;
6              
7             =encoding utf8
8              
9             =head1 NAME
10              
11             Blockchain::Ethereum::ABI::Encoder - Contract ABI argument encoder
12              
13             =head1 SYNOPSIS
14              
15             Allows you to encode contract ABI arguments
16              
17             my $encoder = Blockchain::Ethereum::ABI::Encoder->new();
18             $encoder->function('test')
19             # string
20             ->append(string => 'Hello, World!')
21             # bytes
22             ->append(bytes => unpack("H*", 'Hello, World!'))
23             # tuple
24             ->append('(uint256,address)' => [75000000000000, '0x0000000000000000000000000000000000000000'])
25             # arrays
26             ->append('bool[]', [1, 0, 1, 0])
27             # multidimensional arrays
28             ->append('uint256[][][2]', [[[1]], [[2]]])
29             # tuples arrays and tuples inside tuples
30             ->append('((int256)[2])' => [[[1], [2]]])->encode();
31              
32             =cut
33              
34 5     5   2220 use Carp;
  5         13  
  5         319  
35 5     5   2605 use Crypt::Digest::Keccak256 qw(keccak256_hex);
  5         22397  
  5         330  
36              
37 5     5   2303 use Blockchain::Ethereum::ABI::Type;
  5         13  
  5         253  
38 5     5   2191 use Blockchain::Ethereum::ABI::Type::Tuple;
  5         15  
  5         6374  
39              
40 50     50   114 field $_instances :reader(_instances) :writer(set_instances) = [];
41 50     24 0 366 field $_function_name :reader(_function_name) :writer(set_function_name);
  24     23 0 72  
  24     15   82  
  23         50  
  23         68  
  15         27  
  15         54  
42              
43             =head2 append
44              
45             Appends type signature and the respective values to the encoder.
46              
47             Usage:
48              
49             append(signature => value) -> L
50              
51             =over 4
52              
53             =item * C<%param> key is the respective type signature followed by the value e.g. uint256 => 10
54              
55             =back
56              
57             Returns C<$self>
58              
59             =cut
60              
61 28     28 1 8673 method append (%param) {
  28         48  
  28         83  
  28         54  
62              
63 28         92 for my $type_signature (keys %param) {
64             push(
65             $self->_instances->@*,
66             Blockchain::Ethereum::ABI::Type->new(
67             signature => $type_signature,
68 28         80 data => $param{$type_signature}));
69             }
70              
71 27         176 return $self;
72             }
73              
74             =head2 function
75              
76             Appends the function name to the encoder, this is optional for when you want the
77             function signature added to the encoded string or only the function name encoded.
78              
79             Usage:
80              
81             function(string) -> L
82              
83             =over 4
84              
85             =item * C<$function_name> solidity function name e.g. for `transfer(address,uint256)` will be `transfer`
86              
87             =back
88              
89             Returns C<$self>
90              
91             =cut
92              
93 8     8 1 22112 method function ($function_name) {
  8         26  
  8         20  
  8         14  
94              
95 8         30 $self->set_function_name($function_name);
96 8         61 return $self;
97             }
98              
99             =head2 generate_function_signature
100              
101             Based on the given function name and type signatures create the complete function
102             signature.
103              
104             Usage:
105              
106             generate_function_signature() -> string
107              
108             =over 4
109              
110             =back
111              
112             Returns the function signature string
113              
114             =cut
115              
116 8     8 1 22 method generate_function_signature {
117              
118 8 50       16 croak "Missing function name e.g. ->function('name')" unless $self->_function_name;
119 8         24 my $signature = $self->_function_name . '(';
120 8         23 $signature .= sprintf("%s,", $_->signature) for $self->_instances->@*;
121 8         31 chop $signature;
122 8         51 return $signature . ')';
123             }
124              
125             =head2 encode_function_signature
126              
127             Encode function signature keccak_256/sha3
128              
129             Usage:
130              
131             encode_function_signature('transfer(address,uint)') -> encoded string
132              
133             =over 4
134              
135             =item * C<$signature> (Optional) function signature, if not given, will try to use the appended function name
136              
137             =back
138              
139             Returns the encoded string 0x prefixed
140              
141             =cut
142              
143 8     8 1 25 method encode_function_signature ($signature = undef) {
  8         25  
  8         21  
  8         13  
144              
145 8   33     39 return sprintf("0x%.8s", keccak256_hex($signature // $self->generate_function_signature));
146             }
147              
148             =head2 encode
149              
150             Encodes appended signatures and the function name (when given)
151              
152             Usage:
153              
154             encode() -> encoded string
155              
156             =over 4
157              
158             =back
159              
160             Returns the encoded string, if function name was given will be 0x prefixed
161              
162             =cut
163              
164 14     14 1 34 method encode {
165              
166 14         105 my $tuple = Blockchain::Ethereum::ABI::Type::Tuple->new;
167 14         47 $tuple->set_instances($self->_instances);
168 14         47 my @data = $tuple->encode->@*;
169 8 50       34 unshift @data, $self->encode_function_signature if $self->_function_name;
170              
171 8         242 $self->_clean;
172              
173 8         288 return join('', @data);
174             }
175              
176 15     15   6382 method _clean {
177              
178 15         59 $self->set_instances([]);
179 15         45 $self->set_function_name(undef);
180             }
181              
182             1;
183              
184             __END__