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> Deref for FixedBytes<N> {
191    type Target = [u8; N];
192
193    fn deref(&self) -> &Self::Target {
194        &self.0
195    }
196}
197
198impl<const N: usize> Borrow<[u8; N]> for FixedBytes<N> {
199    fn borrow(&self) -> &[u8; N] {
200        &self.0
201    }
202}
203
204impl<const N: usize> AsRef<[u8; N]> for FixedBytes<N> {
205    fn as_ref(&self) -> &[u8; N] {
206        &self.0
207    }
208}
209
210/// Newtype wrapper for Solidity ABI encoding/decoding `Vec<u8>` as a dynamic sized byte
211/// sequence.
212///
213/// Ref: <https://docs.soliditylang.org/en/latest/types.html#bytes-and-string-as-arrays>
214#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)]
215#[cfg_attr(feature = "std", derive(TypeInfo))]
216#[repr(transparent)]
217pub struct DynBytes(pub Vec<u8>);
218
219impl DynBytes {
220    /// Constructs new empty `DynBytes` without allocating.
221    pub const fn new() -> Self {
222        Self(Vec::new())
223    }
224}
225
226impl DynBytes {
227    /// Converts a reference to `Vec<u8>` into a reference to `DynBytes` (without
228    /// copying).
229    pub fn from_ref(value: &Vec<u8>) -> &Self {
230        // SAFETY: `DynBytes` is `#[repr(transparent)]` for `Vec<u8>`,
231        // so converting from `&Vec<u8>` to `&DynBytes` is sound.
232        unsafe { &*(value as *const Vec<u8>).cast::<Self>() }
233    }
234}
235
236// Implements `SolTypeDecode` and `SolTypeEncode` for `DynBytes`.
237impl SolTypeDecode for DynBytes {
238    type AlloyType = sol_data::Bytes;
239
240    fn detokenize(
241        token: <Self::AlloyType as AlloySolType>::Token<'_>,
242    ) -> Result<Self, Error> {
243        // Converts token directly into `Vec<u8>`, skipping the conversion to
244        // `alloy_sol_types::private::Bytes`, which then has to be converted back to
245        // `Vec<u8>`.
246        Ok(Self(token.into_vec()))
247    }
248}
249
250impl SolTypeEncode for DynBytes {
251    type AlloyType = sol_data::Bytes;
252
253    const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
254
255    fn tokenize(&self) -> Self::TokenType<'_> {
256        self.0.as_slice()
257    }
258}
259
260impl SolTopicEncode for DynBytes {
261    fn encode_topic<H>(&self, hasher: H) -> [u8; 32]
262    where
263        H: Fn(&[u8], &mut [u8; 32]),
264    {
265        let mut output = [0u8; 32];
266        hasher(self.0.as_slice(), &mut output);
267        output
268    }
269
270    fn topic_preimage(&self, buffer: &mut Vec<u8>) {
271        append_non_empty_member_topic_bytes(self.0.as_slice(), buffer);
272    }
273
274    fn default_topic_preimage(buffer: &mut Vec<u8>) {
275        buffer.extend([0u8; 32]);
276    }
277
278    fn topic_preimage_size(&self) -> usize {
279        non_zero_multiple_of_32(self.0.len())
280    }
281
282    fn default_topic_preimage_size() -> usize {
283        32
284    }
285}
286
287impl SolTokenType for DynBytes {
288    type TokenType<'enc> = &'enc [u8];
289
290    type DefaultType = DynSizeDefault;
291}
292
293impl crate::sol::types::private::Sealed for DynBytes {}
294
295// Implements `SolDecode` and `SolEncode` for `DynBytes`.
296impl SolDecode for DynBytes {
297    type SolType = DynBytes;
298
299    fn from_sol_type(value: Self::SolType) -> Result<Self, Error> {
300        Ok(value)
301    }
302}
303
304impl<'a> SolEncode<'a> for DynBytes {
305    type SolType = &'a DynBytes;
306
307    fn to_sol_type(&'a self) -> Self::SolType {
308        self
309    }
310}
311
312// Implements core/standard traits for cheap representations as the inner type.
313impl From<Vec<u8>> for DynBytes {
314    fn from(value: Vec<u8>) -> Self {
315        Self(value)
316    }
317}
318
319impl From<Box<[u8]>> for DynBytes {
320    fn from(value: Box<[u8]>) -> Self {
321        // Converts to `Vec<u8>` without clones or allocation.
322        Self(value.into_vec())
323    }
324}
325
326impl Deref for DynBytes {
327    type Target = [u8];
328
329    fn deref(&self) -> &Self::Target {
330        &self.0
331    }
332}
333
334impl Borrow<[u8]> for DynBytes {
335    fn borrow(&self) -> &[u8] {
336        &self.0
337    }
338}
339
340impl AsRef<[u8]> for DynBytes {
341    fn as_ref(&self) -> &[u8] {
342        &self.0
343    }
344}
345
346/// Newtype wrapper for Solidity ABI encoding a byte slice (i.e. `&[u8]`) as a
347/// dynamic sized byte sequence.
348///
349/// # Note
350///
351/// Only encoding is implemented for this type, see [`DynBytes`] for an equivalent "owned"
352/// representation that supports both encoding and decoding.
353///
354/// Ref: <https://docs.soliditylang.org/en/latest/types.html#bytes-and-string-as-arrays>
355#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode)]
356#[cfg_attr(feature = "std", derive(TypeInfo))]
357#[repr(transparent)]
358pub struct ByteSlice<'a>(pub &'a [u8]);
359
360// Implements `SolTypeEncode` for `ByteSlice`.
361impl SolTypeEncode for ByteSlice<'_> {
362    type AlloyType = sol_data::Bytes;
363
364    const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
365
366    fn tokenize(&self) -> Self::TokenType<'_> {
367        self.0
368    }
369}
370
371impl SolTopicEncode for ByteSlice<'_> {
372    fn encode_topic<H>(&self, hasher: H) -> [u8; 32]
373    where
374        H: Fn(&[u8], &mut [u8; 32]),
375    {
376        let mut output = [0u8; 32];
377        hasher(self.0, &mut output);
378        output
379    }
380
381    fn topic_preimage(&self, buffer: &mut Vec<u8>) {
382        append_non_empty_member_topic_bytes(self.0, buffer);
383    }
384
385    fn default_topic_preimage(buffer: &mut Vec<u8>) {
386        buffer.extend([0u8; 32]);
387    }
388
389    fn topic_preimage_size(&self) -> usize {
390        non_zero_multiple_of_32(self.0.len())
391    }
392
393    fn default_topic_preimage_size() -> usize {
394        32
395    }
396}
397
398impl SolTokenType for ByteSlice<'_> {
399    type TokenType<'enc> = &'enc [u8];
400
401    type DefaultType = DynSizeDefault;
402}
403
404impl crate::sol::types::private::Sealed for ByteSlice<'_> {}
405
406// Implements `SolEncode` for `ByteSlice`.
407impl<'a> SolEncode<'a> for ByteSlice<'a> {
408    type SolType = &'a ByteSlice<'a>;
409
410    fn to_sol_type(&'a self) -> Self::SolType {
411        self
412    }
413}
414
415// Implements core/standard traits for cheap representations as the inner type.
416impl<'a> From<&'a [u8]> for ByteSlice<'a> {
417    fn from(value: &'a [u8]) -> Self {
418        Self(value)
419    }
420}
421
422impl Deref for ByteSlice<'_> {
423    type Target = [u8];
424
425    fn deref(&self) -> &Self::Target {
426        self.0
427    }
428}
429
430impl Borrow<[u8]> for ByteSlice<'_> {
431    fn borrow(&self) -> &[u8] {
432        self.0
433    }
434}
435
436impl AsRef<[u8]> for ByteSlice<'_> {
437    fn as_ref(&self) -> &[u8] {
438        self.0
439    }
440}