@@ -9,33 +9,39 @@ defmodule ABI.TypeEncoder do
9
9
10
10
@ doc """
11
11
Encodes the given data based on the function selector.
12
+
13
+ ## Parameters
14
+ - data: The data to encode
15
+ - selector_or_types: Either a FunctionSelector struct or a list of types to encode the data with
16
+ - data_type: Determines which types to use from a FunctionSelector struct. Can be `:input` or `:output`.
17
+ - mode: Encoding mode. Can be `:standard` or `:packed`.
12
18
"""
13
19
14
- def encode ( data , selector_or_types , data_type \\ :input )
20
+ def encode ( data , selector_or_types , data_type \\ :input , mode \\ :standard )
15
21
16
- def encode ( data , % FunctionSelector { function: nil , types: types } , :input ) do
17
- do_encode ( data , types )
22
+ def encode ( data , % FunctionSelector { function: nil , types: types } , :input , mode ) do
23
+ do_encode ( data , types , mode )
18
24
end
19
25
20
- def encode ( data , % FunctionSelector { types: types } = function_selector , :input ) do
21
- encode_method_id ( function_selector ) <> do_encode ( data , types )
26
+ def encode ( data , % FunctionSelector { types: types } = function_selector , :input , mode ) do
27
+ encode_method_id ( function_selector ) <> do_encode ( data , types , mode )
22
28
end
23
29
24
- def encode ( data , % FunctionSelector { returns: types } , :output ) do
25
- do_encode ( data , types )
30
+ def encode ( data , % FunctionSelector { returns: types } , :output , mode ) do
31
+ do_encode ( data , types , mode )
26
32
end
27
33
28
- def encode ( data , types , _ ) when is_list ( types ) do
29
- do_encode ( data , types )
34
+ def encode ( data , types , _ , mode ) when is_list ( types ) do
35
+ do_encode ( data , types , mode )
30
36
end
31
37
32
- def encode_raw ( data , types ) when is_list ( types ) do
33
- do_encode ( data , types )
38
+ def encode_raw ( data , types , mode ) when is_list ( types ) do
39
+ do_encode ( data , types , mode )
34
40
end
35
41
36
- defp do_encode ( params , types , static_acc \\ [ ] , dynamic_acc \\ [ ] )
42
+ defp do_encode ( params , types , static_acc \\ [ ] , dynamic_acc \\ [ ] , mode )
37
43
38
- defp do_encode ( [ ] , [ ] , reversed_static_acc , reversed_dynamic_acc ) do
44
+ defp do_encode ( [ ] , [ ] , reversed_static_acc , reversed_dynamic_acc , :standard ) do
39
45
static_acc = Enum . reverse ( reversed_static_acc )
40
46
dynamic_acc = Enum . reverse ( reversed_dynamic_acc )
41
47
@@ -61,104 +67,131 @@ defmodule ABI.TypeEncoder do
61
67
{ complete_static_part , _ } =
62
68
Enum . reduce ( dynamic_indexes , { static_acc , static_part_size } , fn { index , byte_size } ,
63
69
{ acc , size_acc } ->
64
- new_static_acc = List . replace_at ( acc , index , encode_uint ( size_acc , 256 ) )
65
- new_prefix_size = byte_size + size_acc
70
+ new_static_acc = List . replace_at ( acc , index , encode_uint ( size_acc , 256 , :standard ) )
66
71
67
- { new_static_acc , new_prefix_size }
72
+ { new_static_acc , byte_size + size_acc }
68
73
end )
69
74
70
75
Enum . join ( complete_static_part ++ dynamic_acc )
71
76
end
72
77
78
+ defp do_encode ( [ ] , [ ] , static_acc , dynamic_acc , :packed ) do
79
+ { values_acc , [ ] } =
80
+ Enum . reduce ( static_acc , { [ ] , dynamic_acc } , fn
81
+ { :dynamic , _ } , { values_acc , [ value | dynamic_acc ] } ->
82
+ { [ value | values_acc ] , dynamic_acc }
83
+
84
+ value , { values_acc , dynamic_acc } ->
85
+ { [ value | values_acc ] , dynamic_acc }
86
+ end )
87
+
88
+ Enum . join ( values_acc )
89
+ end
90
+
73
91
defp do_encode (
74
92
[ current_parameter | remaining_parameters ] ,
75
93
[ current_type | remaining_types ] ,
76
94
static_acc ,
77
- dynamic_acc
95
+ dynamic_acc ,
96
+ mode
78
97
) do
79
98
{ new_static_acc , new_dynamic_acc } =
80
- do_encode_type ( current_type , current_parameter , static_acc , dynamic_acc )
99
+ do_encode_type ( current_type , current_parameter , static_acc , dynamic_acc , mode )
81
100
82
- do_encode ( remaining_parameters , remaining_types , new_static_acc , new_dynamic_acc )
101
+ do_encode ( remaining_parameters , remaining_types , new_static_acc , new_dynamic_acc , mode )
83
102
end
84
103
85
- defp do_encode_type ( :bool , parameter , static_part , dynamic_part ) do
104
+ defp do_encode_type ( :bool , parameter , static_part , dynamic_part , mode ) do
86
105
value =
87
106
case parameter do
88
- true -> encode_uint ( 1 , 8 )
89
- false -> encode_uint ( 0 , 8 )
107
+ true -> encode_uint ( 1 , 8 , mode )
108
+ false -> encode_uint ( 0 , 8 , mode )
90
109
_ -> raise "Invalid data for bool: #{ inspect ( parameter ) } "
91
110
end
92
111
93
112
{ [ value | static_part ] , dynamic_part }
94
113
end
95
114
96
- defp do_encode_type ( { :uint , size } , parameter , static_part , dynamic_part ) do
97
- value = encode_uint ( parameter , size )
115
+ defp do_encode_type ( { :uint , size } , parameter , static_part , dynamic_part , mode ) do
116
+ value = encode_uint ( parameter , size , mode )
98
117
99
118
{ [ value | static_part ] , dynamic_part }
100
119
end
101
120
102
- defp do_encode_type ( { :int , size } , parameter , static_part , dynamic_part ) do
103
- value = encode_int ( parameter , size )
121
+ defp do_encode_type ( { :int , size } , parameter , static_part , dynamic_part , mode ) do
122
+ value = encode_int ( parameter , size , mode )
104
123
105
124
{ [ value | static_part ] , dynamic_part }
106
125
end
107
126
108
- defp do_encode_type ( :string , parameter , static_part , dynamic_part ) do
109
- do_encode_type ( :bytes , parameter , static_part , dynamic_part )
127
+ defp do_encode_type ( :string , parameter , static_part , dynamic_part , mode ) do
128
+ do_encode_type ( :bytes , parameter , static_part , dynamic_part , mode )
110
129
end
111
130
112
- defp do_encode_type ( :bytes , parameter , static_part , dynamic_part ) do
131
+ defp do_encode_type ( :bytes , parameter , static_part , dynamic_part , mode ) do
113
132
binary_param = maybe_encode_unsigned ( parameter )
114
- value = encode_uint ( byte_size ( binary_param ) , 256 ) <> encode_bytes ( binary_param )
133
+
134
+ value =
135
+ case mode do
136
+ :standard ->
137
+ encode_uint ( byte_size ( binary_param ) , 256 , mode ) <> encode_bytes ( binary_param , mode )
138
+
139
+ :packed ->
140
+ encode_bytes ( binary_param , mode )
141
+ end
115
142
116
143
dynamic_part_byte_size = byte_size ( value )
117
144
118
145
{ [ { :dynamic , dynamic_part_byte_size } | static_part ] , [ value | dynamic_part ] }
119
146
end
120
147
121
- defp do_encode_type ( { :bytes , size } , parameter , static_part , dynamic_part )
148
+ defp do_encode_type ( { :bytes , size } , parameter , static_part , dynamic_part , mode )
122
149
when is_binary ( parameter ) and byte_size ( parameter ) <= size do
123
- value = encode_bytes ( parameter )
150
+ value = encode_bytes ( parameter , mode )
124
151
125
152
{ [ value | static_part ] , dynamic_part }
126
153
end
127
154
128
- defp do_encode_type ( { :bytes , size } , data , _ , _ ) when is_binary ( data ) do
155
+ defp do_encode_type ( { :bytes , size } , data , _ , _ , _ ) when is_binary ( data ) do
129
156
raise "size mismatch for bytes#{ size } : #{ inspect ( data ) } "
130
157
end
131
158
132
- defp do_encode_type ( { :bytes , size } , data , static_part , dynamic_part ) when is_integer ( data ) do
159
+ defp do_encode_type ( { :bytes , size } , data , static_part , dynamic_part , mode )
160
+ when is_integer ( data ) do
133
161
binary_param = maybe_encode_unsigned ( data )
134
162
135
- do_encode_type ( { :bytes , size } , binary_param , static_part , dynamic_part )
163
+ do_encode_type ( { :bytes , size } , binary_param , static_part , dynamic_part , mode )
136
164
end
137
165
138
- defp do_encode_type ( { :bytes , size } , data , _ , _ ) do
166
+ defp do_encode_type ( { :bytes , size } , data , _ , _ , _ ) do
139
167
raise "wrong datatype for bytes#{ size } : #{ inspect ( data ) } "
140
168
end
141
169
142
- defp do_encode_type ( { :array , type } , data , static_acc , dynamic_acc ) do
170
+ defp do_encode_type ( { :array , type } , data , static_acc , dynamic_acc , mode ) do
143
171
param_count = Enum . count ( data )
144
172
145
- encoded_size = encode_uint ( param_count , 256 )
146
-
147
173
types = List . duplicate ( type , param_count )
148
174
149
- result = do_encode ( data , types )
175
+ result = do_encode ( data , types , mode )
150
176
151
- dynamic_acc_with_size = [ encoded_size | dynamic_acc ]
177
+ { dynamic_acc_with_size , data_bytes_size } =
178
+ case mode do
179
+ :standard ->
180
+ encoded_size = encode_uint ( param_count , 256 , mode )
181
+ # length is included and also length size is added
182
+ { [ encoded_size | dynamic_acc ] , byte_size ( result ) + 32 }
152
183
153
- # number of elements count + data size
154
- data_bytes_size = byte_size ( result ) + 32
184
+ :packed ->
185
+ # ignoring length of array
186
+ { dynamic_acc , byte_size ( result ) }
187
+ end
155
188
156
189
{ [ { :dynamic , data_bytes_size } | static_acc ] , [ result | dynamic_acc_with_size ] }
157
190
end
158
191
159
- defp do_encode_type ( { :array , type , size } , data , static_acc , dynamic_acc ) do
192
+ defp do_encode_type ( { :array , type , size } , data , static_acc , dynamic_acc , mode ) do
160
193
types = List . duplicate ( type , size )
161
- result = do_encode ( data , types )
194
+ result = do_encode ( data , types , mode )
162
195
163
196
if FunctionSelector . is_dynamic? ( type ) do
164
197
data_bytes_size = byte_size ( result )
@@ -169,20 +202,30 @@ defmodule ABI.TypeEncoder do
169
202
end
170
203
end
171
204
172
- defp do_encode_type ( :address , data , static_acc , dynamic_acc ) do
173
- do_encode_type ( { :uint , 160 } , data , static_acc , dynamic_acc )
205
+ defp do_encode_type ( :address , data , static_acc , dynamic_acc , mode ) do
206
+ do_encode_type ( { :uint , 160 } , data , static_acc , dynamic_acc , mode )
207
+ end
208
+
209
+ defp do_encode_type ( { :tuple , _types } , _ , _ , _ , :packed ) do
210
+ raise RuntimeError , "Structs (tuples) are not supported in packed mode encoding"
174
211
end
175
212
176
- defp do_encode_type ( type = { :tuple , _types } , tuple_parameters , static_acc , dynamic_acc )
213
+ defp do_encode_type (
214
+ type = { :tuple , _types } ,
215
+ tuple_parameters ,
216
+ static_acc ,
217
+ dynamic_acc ,
218
+ :standard
219
+ )
177
220
when is_tuple ( tuple_parameters ) do
178
221
list_parameters = Tuple . to_list ( tuple_parameters )
179
222
180
- do_encode_type ( type , list_parameters , static_acc , dynamic_acc )
223
+ do_encode_type ( type , list_parameters , static_acc , dynamic_acc , :standard )
181
224
end
182
225
183
- defp do_encode_type ( type = { :tuple , types } , list_parameters , static_acc , dynamic_acc )
226
+ defp do_encode_type ( type = { :tuple , types } , list_parameters , static_acc , dynamic_acc , :standard )
184
227
when is_list ( list_parameters ) do
185
- result = do_encode ( list_parameters , types )
228
+ result = do_encode ( list_parameters , types , :standard )
186
229
187
230
if FunctionSelector . is_dynamic? ( type ) do
188
231
data_bytes_size = byte_size ( result )
@@ -193,8 +236,8 @@ defmodule ABI.TypeEncoder do
193
236
end
194
237
end
195
238
196
- defp encode_bytes ( bytes ) do
197
- pad ( bytes , byte_size ( bytes ) , :right )
239
+ defp encode_bytes ( bytes , mode ) do
240
+ pad ( bytes , byte_size ( bytes ) , :right , mode )
198
241
end
199
242
200
243
@ spec encode_method_id ( FunctionSelector . t ( ) ) :: binary ( )
@@ -216,7 +259,7 @@ defmodule ABI.TypeEncoder do
216
259
217
260
# Note, we'll accept a binary or an integer here, so long as the
218
261
# binary is not longer than our allowed data size
219
- defp encode_uint ( data , size_in_bits ) when rem ( size_in_bits , 8 ) == 0 do
262
+ defp encode_uint ( data , size_in_bits , mode ) when rem ( size_in_bits , 8 ) == 0 do
220
263
size_in_bytes = ( size_in_bits / 8 ) |> round
221
264
bin = maybe_encode_unsigned ( data )
222
265
@@ -226,22 +269,22 @@ defmodule ABI.TypeEncoder do
226
269
"Data overflow encoding uint, data `#{ data } ` cannot fit in #{ size_in_bytes * 8 } bits"
227
270
)
228
271
229
- bin |> pad ( size_in_bytes , :left )
272
+ bin |> pad ( size_in_bytes , :left , mode )
230
273
end
231
274
232
- defp encode_int ( data , size_in_bits ) when rem ( size_in_bits , 8 ) == 0 do
275
+ defp encode_int ( data , size_in_bits , mode ) when rem ( size_in_bits , 8 ) == 0 do
233
276
if signed_overflow? ( data , size_in_bits ) do
234
277
raise ( "Data overflow encoding int, data `#{ data } ` cannot fit in #{ size_in_bits } bits" )
235
278
end
236
279
237
- encode_int ( data )
280
+ case mode do
281
+ :standard -> << data :: signed - 256 >>
282
+ :packed -> << data :: signed - size ( size_in_bits ) >>
283
+ end
238
284
end
239
285
240
- # encoding with integer-signed-256 we already get the right padding
241
- defp encode_int ( data ) , do: << data :: signed - 256 >>
242
-
243
286
defp signed_overflow? ( n , max_bits ) do
244
- n < :math . pow ( 2 , max_bits - 1 ) * - 1 + 1 || n > :math . pow ( 2 , max_bits - 1 ) - 1
287
+ n < 2 ** ( max_bits - 1 ) * - 1 + 1 || n > 2 ** ( max_bits - 1 ) - 1
245
288
end
246
289
247
290
def mod ( x , n ) do
@@ -252,7 +295,19 @@ defmodule ABI.TypeEncoder do
252
295
else: remainder
253
296
end
254
297
255
- defp pad ( bin , size_in_bytes , direction ) do
298
+ defp pad ( bin , size_in_bytes , _direction , :packed ) when byte_size ( bin ) == size_in_bytes , do: bin
299
+
300
+ defp pad ( bin , size_in_bytes , direction , :packed ) when byte_size ( bin ) < size_in_bytes do
301
+ padding_size_bits = ( size_in_bytes - byte_size ( bin ) ) * 8
302
+ padding = << 0 :: size ( padding_size_bits ) >>
303
+
304
+ case direction do
305
+ :left -> padding <> bin
306
+ :right -> bin <> padding
307
+ end
308
+ end
309
+
310
+ defp pad ( bin , size_in_bytes , direction , :standard ) do
256
311
total_size = size_in_bytes + mod ( 32 - size_in_bytes , 32 )
257
312
padding_size_bits = ( total_size - byte_size ( bin ) ) * 8
258
313
padding = << 0 :: size ( padding_size_bits ) >>
0 commit comments