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