ink_primitives/sol/
bytes.rs

1// Copyright (C) ink! contributors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use core::{
16    borrow::Borrow,
17    ops::Deref,
18};
19
20use alloy_sol_types::{
21    SolType as AlloySolType,
22    sol_data,
23};
24use ink_prelude::{
25    boxed::Box,
26    vec::Vec,
27};
28use scale::{
29    Decode,
30    Encode,
31};
32#[cfg(feature = "std")]
33use scale_info::TypeInfo;
34
35use crate::sol::{
36    Error,
37    SolDecode,
38    SolEncode,
39    SolTopicEncode,
40    SolTypeDecode,
41    SolTypeEncode,
42    encodable::{
43        DynSizeDefault,
44        FixedSizeDefault,
45        Word,
46    },
47    types::SolTokenType,
48    utils::{
49        append_non_empty_member_topic_bytes,
50        non_zero_multiple_of_32,
51    },
52};
53
54/// Newtype wrapper for Solidity ABI encoding/decoding `[u8; N]` for `1 <= N <= 32` as
55/// fixed-size byte sequences.
56///
57/// Ref: <https://docs.soliditylang.org/en/latest/types.html#fixed-size-byte-arrays>
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
59#[cfg_attr(feature = "std", derive(TypeInfo))]
60#[repr(transparent)]
61pub struct FixedBytes<const N: usize>(pub [u8; N]);
62
63impl<const N: usize> FixedBytes<N> {
64    /// Converts a reference to `[u8; N]` into a reference to `FixedBytes<N>` (without
65    /// copying).
66    pub fn from_ref(value: &[u8; N]) -> &Self {
67        // SAFETY: `FixedBytes<N>` is `#[repr(transparent)]` for `[u8; N]`,
68        // so converting from `&[u8; N]` to `&FixedBytes<N>` is sound.
69        unsafe { &*value.as_ptr().cast::<Self>() }
70    }
71}
72
73// Implements `SolTypeDecode` and `SolTypeEncode` for `FixedBytes<N>`.
74impl<const N: usize> SolTypeDecode for FixedBytes<N>
75where
76    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
77{
78    type AlloyType = sol_data::FixedBytes<N>;
79
80    fn detokenize(
81        token: <Self::AlloyType as AlloySolType>::Token<'_>,
82    ) -> Result<Self, Error> {
83        // Converts token directly into `[u8; N]`, skipping the conversion to
84        // `alloy_sol_types::private::FixedBytes`, which would then has to be
85        // unpacked to `[u8; N]`.
86        // Ref: <https://github.com/alloy-rs/core/blob/5ae4fe0b246239602c97cc5a2f2e4bc780e2024a/crates/sol-types/src/types/data_type.rs#L204-L206>
87        Ok(Self(
88            token.0.0[..N]
89                .try_into()
90                .expect("Expected a slice of N bytes"),
91        ))
92    }
93}
94
95impl<const N: usize> SolTypeEncode for FixedBytes<N>
96where
97    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
98{
99    type AlloyType = sol_data::FixedBytes<N>;
100
101    const DEFAULT_VALUE: Self::DefaultType = FixedSizeDefault::WORD;
102
103    fn tokenize(&self) -> Self::TokenType<'_> {
104        // Direct implementation simplifies generic implementations by removing
105        // requirement for `SolTypeValue<Self::AlloyType>`.
106        let mut word = [0; 32];
107        word[..N].copy_from_slice(self.0.as_slice());
108        Word(word)
109    }
110}
111
112impl<const N: usize> SolTopicEncode for FixedBytes<N>
113where
114    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
115{
116    fn encode_topic<H>(&self, _: H) -> [u8; 32]
117    where
118        H: Fn(&[u8], &mut [u8; 32]),
119    {
120        self.tokenize().0
121    }
122
123    fn topic_preimage(&self, buffer: &mut Vec<u8>) {
124        buffer.extend(self.tokenize().0);
125    }
126
127    fn default_topic_preimage(buffer: &mut Vec<u8>) {
128        buffer.extend([0u8; 32]);
129    }
130
131    fn topic_preimage_size(&self) -> usize {
132        Self::default_topic_preimage_size()
133    }
134
135    fn default_topic_preimage_size() -> usize {
136        32
137    }
138}
139
140impl<const N: usize> SolTokenType for FixedBytes<N>
141where
142    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
143{
144    type TokenType<'enc> = Word;
145
146    type DefaultType = FixedSizeDefault;
147}
148
149impl<const N: usize> crate::sol::types::private::Sealed for FixedBytes<N> where
150    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes
151{
152}
153
154// Implements `SolDecode` and `SolEncode` for `FixedBytes<N>`.
155impl<const N: usize> SolDecode for FixedBytes<N>
156where
157    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
158{
159    type SolType = FixedBytes<N>;
160
161    fn from_sol_type(value: Self::SolType) -> Result<Self, Error> {
162        Ok(value)
163    }
164}
165
166impl<'a, const N: usize> SolEncode<'a> for FixedBytes<N>
167where
168    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
169{
170    type SolType = &'a FixedBytes<N>;
171
172    fn to_sol_type(&'a self) -> Self::SolType {
173        self
174    }
175}
176
177// Implements core/standard traits for cheap representations as the inner type.
178impl<const N: usize> From<[u8; N]> for FixedBytes<N> {
179    fn from(value: [u8; N]) -> Self {
180        Self(value)
181    }
182}
183
184impl From<u8> for FixedBytes<1> {
185    fn from(value: u8) -> Self {
186        Self([value; 1])
187    }
188}
189
190impl<const N: usize> From<FixedBytes<N>> for [u8; N] {
191    fn from(value: FixedBytes<N>) -> Self {
192        value.0
193    }
194}
195
196impl From<FixedBytes<1>> for u8 {
197    fn from(value: FixedBytes<1>) -> Self {
198        value.0[0]
199    }
200}
201
202impl<const N: usize> Deref for FixedBytes<N> {
203    type Target = [u8; N];
204
205    fn deref(&self) -> &Self::Target {
206        &self.0
207    }
208}
209
210impl<const N: usize> Borrow<[u8; N]> for FixedBytes<N> {
211    fn borrow(&self) -> &[u8; N] {
212        &self.0
213    }
214}
215
216impl<const N: usize> AsRef<[u8; N]> for FixedBytes<N> {
217    fn as_ref(&self) -> &[u8; N] {
218        &self.0
219    }
220}
221
222/// Newtype wrapper for Solidity ABI encoding/decoding `Vec<u8>` as a dynamic sized byte
223/// sequence.
224///
225/// Ref: <https://docs.soliditylang.org/en/latest/types.html#bytes-and-string-as-arrays>
226#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)]
227#[cfg_attr(feature = "std", derive(TypeInfo))]
228#[repr(transparent)]
229pub struct DynBytes(pub Vec<u8>);
230
231impl DynBytes {
232    /// Constructs new empty `DynBytes` without allocating.
233    pub const fn new() -> Self {
234        Self(Vec::new())
235    }
236}
237
238impl DynBytes {
239    /// Converts a reference to `Vec<u8>` into a reference to `DynBytes` (without
240    /// copying).
241    pub fn from_ref(value: &Vec<u8>) -> &Self {
242        // SAFETY: `DynBytes` is `#[repr(transparent)]` for `Vec<u8>`,
243        // so converting from `&Vec<u8>` to `&DynBytes` is sound.
244        unsafe { &*(value as *const Vec<u8>).cast::<Self>() }
245    }
246}
247
248// Implements `SolTypeDecode` and `SolTypeEncode` for `DynBytes`.
249impl SolTypeDecode for DynBytes {
250    type AlloyType = sol_data::Bytes;
251
252    fn detokenize(
253        token: <Self::AlloyType as AlloySolType>::Token<'_>,
254    ) -> Result<Self, Error> {
255        // Converts token directly into `Vec<u8>`, skipping the conversion to
256        // `alloy_sol_types::private::Bytes`, which then has to be converted back to
257        // `Vec<u8>`.
258        Ok(Self(token.into_vec()))
259    }
260}
261
262impl SolTypeEncode for DynBytes {
263    type AlloyType = sol_data::Bytes;
264
265    const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
266
267    fn tokenize(&self) -> Self::TokenType<'_> {
268        self.0.as_slice()
269    }
270}
271
272impl SolTopicEncode for DynBytes {
273    fn encode_topic<H>(&self, hasher: H) -> [u8; 32]
274    where
275        H: Fn(&[u8], &mut [u8; 32]),
276    {
277        let mut output = [0u8; 32];
278        hasher(self.0.as_slice(), &mut output);
279        output
280    }
281
282    fn topic_preimage(&self, buffer: &mut Vec<u8>) {
283        append_non_empty_member_topic_bytes(self.0.as_slice(), buffer);
284    }
285
286    fn default_topic_preimage(buffer: &mut Vec<u8>) {
287        buffer.extend([0u8; 32]);
288    }
289
290    fn topic_preimage_size(&self) -> usize {
291        non_zero_multiple_of_32(self.0.len())
292    }
293
294    fn default_topic_preimage_size() -> usize {
295        32
296    }
297}
298
299impl SolTokenType for DynBytes {
300    type TokenType<'enc> = &'enc [u8];
301
302    type DefaultType = DynSizeDefault;
303}
304
305impl crate::sol::types::private::Sealed for DynBytes {}
306
307// Implements `SolDecode` and `SolEncode` for `DynBytes`.
308impl SolDecode for DynBytes {
309    type SolType = DynBytes;
310
311    fn from_sol_type(value: Self::SolType) -> Result<Self, Error> {
312        Ok(value)
313    }
314}
315
316impl<'a> SolEncode<'a> for DynBytes {
317    type SolType = &'a DynBytes;
318
319    fn to_sol_type(&'a self) -> Self::SolType {
320        self
321    }
322}
323
324// Implements core/standard traits for cheap representations as the inner type.
325impl From<Vec<u8>> for DynBytes {
326    fn from(value: Vec<u8>) -> Self {
327        Self(value)
328    }
329}
330
331impl From<Box<[u8]>> for DynBytes {
332    fn from(value: Box<[u8]>) -> Self {
333        // Converts to `Vec<u8>` without clones or allocation.
334        Self(value.into_vec())
335    }
336}
337
338impl From<DynBytes> for Vec<u8> {
339    fn from(value: DynBytes) -> Self {
340        value.0
341    }
342}
343
344impl Deref for DynBytes {
345    type Target = [u8];
346
347    fn deref(&self) -> &Self::Target {
348        &self.0
349    }
350}
351
352impl Borrow<[u8]> for DynBytes {
353    fn borrow(&self) -> &[u8] {
354        &self.0
355    }
356}
357
358impl AsRef<[u8]> for DynBytes {
359    fn as_ref(&self) -> &[u8] {
360        &self.0
361    }
362}
363
364/// Newtype wrapper for Solidity ABI encoding a byte slice (i.e. `&[u8]`) as a
365/// dynamic sized byte sequence.
366///
367/// # Note
368///
369/// Only encoding is implemented for this type, see [`DynBytes`] for an equivalent "owned"
370/// representation that supports both encoding and decoding.
371///
372/// Ref: <https://docs.soliditylang.org/en/latest/types.html#bytes-and-string-as-arrays>
373#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode)]
374#[cfg_attr(feature = "std", derive(TypeInfo))]
375#[repr(transparent)]
376pub struct ByteSlice<'a>(pub &'a [u8]);
377
378// Implements `SolTypeEncode` for `ByteSlice`.
379impl SolTypeEncode for ByteSlice<'_> {
380    type AlloyType = sol_data::Bytes;
381
382    const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
383
384    fn tokenize(&self) -> Self::TokenType<'_> {
385        self.0
386    }
387}
388
389impl SolTopicEncode for ByteSlice<'_> {
390    fn encode_topic<H>(&self, hasher: H) -> [u8; 32]
391    where
392        H: Fn(&[u8], &mut [u8; 32]),
393    {
394        let mut output = [0u8; 32];
395        hasher(self.0, &mut output);
396        output
397    }
398
399    fn topic_preimage(&self, buffer: &mut Vec<u8>) {
400        append_non_empty_member_topic_bytes(self.0, buffer);
401    }
402
403    fn default_topic_preimage(buffer: &mut Vec<u8>) {
404        buffer.extend([0u8; 32]);
405    }
406
407    fn topic_preimage_size(&self) -> usize {
408        non_zero_multiple_of_32(self.0.len())
409    }
410
411    fn default_topic_preimage_size() -> usize {
412        32
413    }
414}
415
416impl SolTokenType for ByteSlice<'_> {
417    type TokenType<'enc> = &'enc [u8];
418
419    type DefaultType = DynSizeDefault;
420}
421
422impl crate::sol::types::private::Sealed for ByteSlice<'_> {}
423
424// Implements `SolEncode` for `ByteSlice`.
425impl<'a> SolEncode<'a> for ByteSlice<'_> {
426    type SolType = &'a ByteSlice<'a>;
427
428    fn to_sol_type(&'a self) -> Self::SolType {
429        self
430    }
431}
432
433// Implements core/standard traits for cheap representations as the inner type.
434impl<'a> From<&'a [u8]> for ByteSlice<'a> {
435    fn from(value: &'a [u8]) -> Self {
436        Self(value)
437    }
438}
439
440impl<'a> From<ByteSlice<'a>> for &'a [u8] {
441    fn from(value: ByteSlice<'a>) -> Self {
442        value.0
443    }
444}
445
446impl Deref for ByteSlice<'_> {
447    type Target = [u8];
448
449    fn deref(&self) -> &Self::Target {
450        self.0
451    }
452}
453
454impl Borrow<[u8]> for ByteSlice<'_> {
455    fn borrow(&self) -> &[u8] {
456        self.0
457    }
458}
459
460impl AsRef<[u8]> for ByteSlice<'_> {
461    fn as_ref(&self) -> &[u8] {
462        self.0
463    }
464}