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    Error,
46    SolDecode,
47    SolEncode,
48    SolTypeDecode,
49    SolTypeEncode,
50};
51
52/// Newtype wrapper for Solidity ABI encoding/decoding `[u8; N]` for `1 <= N <= 32` as
53/// fixed-size byte sequences.
54///
55/// Ref: <https://docs.soliditylang.org/en/latest/types.html#fixed-size-byte-arrays>
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
57#[cfg_attr(feature = "std", derive(TypeInfo))]
58#[repr(transparent)]
59pub struct FixedBytes<const N: usize>(pub [u8; N]);
60
61// Implements `SolTypeDecode` and `SolTypeEncode` for `FixedBytes<N>`.
62impl<const N: usize> SolTypeDecode for FixedBytes<N>
63where
64    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
65{
66    type AlloyType = sol_data::FixedBytes<N>;
67
68    fn detokenize(
69        token: <Self::AlloyType as AlloySolType>::Token<'_>,
70    ) -> Result<Self, Error> {
71        // Converts token directly into `[u8; N]`, skipping the conversion to
72        // `alloy_sol_types::private::FixedBytes`, which would then has to be
73        // unpacked to `[u8; N]`.
74        // Ref: <https://github.com/alloy-rs/core/blob/5ae4fe0b246239602c97cc5a2f2e4bc780e2024a/crates/sol-types/src/types/data_type.rs#L204-L206>
75        Ok(Self(
76            token.0 .0[..N]
77                .try_into()
78                .expect("Expected a slice of N bytes"),
79        ))
80    }
81}
82
83impl<const N: usize> SolTypeEncode for FixedBytes<N>
84where
85    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
86{
87    type AlloyType = sol_data::FixedBytes<N>;
88
89    const DEFAULT_VALUE: Self::DefaultType = FixedSizeDefault::WORD;
90
91    fn tokenize(&self) -> Self::TokenType<'_> {
92        // Direct implementation simplifies generic implementations by removing
93        // requirement for `SolTypeValue<Self::AlloyType>`.
94        let mut word = [0; 32];
95        word[..N].copy_from_slice(self.0.as_slice());
96        WordToken::from(word)
97    }
98}
99
100impl<const N: usize> SolTokenType for FixedBytes<N>
101where
102    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
103{
104    type TokenType<'enc> = WordToken;
105
106    type DefaultType = FixedSizeDefault;
107}
108
109impl<const N: usize> crate::sol::types::private::Sealed for FixedBytes<N> where
110    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes
111{
112}
113
114// Implements `SolDecode` and `SolEncode` for `FixedBytes<N>`.
115impl<const N: usize> SolDecode for FixedBytes<N>
116where
117    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
118{
119    type SolType = FixedBytes<N>;
120
121    fn from_sol_type(value: Self::SolType) -> Result<Self, Error> {
122        Ok(value)
123    }
124}
125
126impl<'a, const N: usize> SolEncode<'a> for FixedBytes<N>
127where
128    sol_data::ByteCount<N>: sol_data::SupportedFixedBytes,
129{
130    type SolType = &'a FixedBytes<N>;
131
132    fn to_sol_type(&'a self) -> Self::SolType {
133        self
134    }
135}
136
137// Implements core/standard traits for cheap representations as the inner type.
138impl<const N: usize> From<[u8; N]> for FixedBytes<N> {
139    fn from(value: [u8; N]) -> Self {
140        Self(value)
141    }
142}
143
144impl From<u8> for FixedBytes<1> {
145    fn from(value: u8) -> Self {
146        Self([value; 1])
147    }
148}
149
150impl<const N: usize> Deref for FixedBytes<N> {
151    type Target = [u8; N];
152
153    fn deref(&self) -> &Self::Target {
154        &self.0
155    }
156}
157
158impl<const N: usize> Borrow<[u8; N]> for FixedBytes<N> {
159    fn borrow(&self) -> &[u8; N] {
160        &self.0
161    }
162}
163
164impl<const N: usize> AsRef<[u8; N]> for FixedBytes<N> {
165    fn as_ref(&self) -> &[u8; N] {
166        &self.0
167    }
168}
169
170/// Newtype wrapper for Solidity ABI encoding/decoding `Vec<u8>` as dynamic sized byte
171/// sequences.
172///
173/// Ref: <https://docs.soliditylang.org/en/latest/types.html#bytes-and-string-as-arrays>
174#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode)]
175#[cfg_attr(feature = "std", derive(TypeInfo))]
176#[repr(transparent)]
177pub struct DynBytes(pub Vec<u8>);
178
179impl DynBytes {
180    /// Constructs new empty `DynBytes` without allocating.
181    pub const fn new() -> Self {
182        Self(Vec::new())
183    }
184}
185
186// Implements `SolTypeDecode` and `SolTypeEncode` for `DynBytes`.
187impl SolTypeDecode for DynBytes {
188    type AlloyType = sol_data::Bytes;
189
190    fn detokenize(
191        token: <Self::AlloyType as AlloySolType>::Token<'_>,
192    ) -> Result<Self, Error> {
193        // Converts token directly into `Vec<u8>`, skipping the conversion to
194        // `alloy_sol_types::private::Bytes`, which then has to be converted back to
195        // `Vec<u8>`.
196        Ok(Self(token.into_vec()))
197    }
198}
199
200impl SolTypeEncode for DynBytes {
201    type AlloyType = sol_data::Bytes;
202
203    const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
204
205    fn tokenize(&self) -> Self::TokenType<'_> {
206        // Direct implementation simplifies generic implementations by removing
207        // requirement for `SolTypeValue<Self::AlloyType>`.
208        PackedSeqToken(self.0.as_slice())
209    }
210}
211
212impl SolTokenType for DynBytes {
213    type TokenType<'enc> = PackedSeqToken<'enc>;
214
215    type DefaultType = DynSizeDefault;
216}
217
218impl crate::sol::types::private::Sealed for DynBytes {}
219
220// Implements `SolDecode` and `SolEncode` for `DynBytes`.
221impl SolDecode for DynBytes {
222    type SolType = DynBytes;
223
224    fn from_sol_type(value: Self::SolType) -> Result<Self, Error> {
225        Ok(value)
226    }
227}
228
229impl<'a> SolEncode<'a> for DynBytes {
230    type SolType = &'a DynBytes;
231
232    fn to_sol_type(&'a self) -> Self::SolType {
233        self
234    }
235}
236
237// Implements core/standard traits for cheap representations as the inner type.
238impl From<Vec<u8>> for DynBytes {
239    fn from(value: Vec<u8>) -> Self {
240        Self(value)
241    }
242}
243
244impl From<Box<[u8]>> for DynBytes {
245    fn from(value: Box<[u8]>) -> Self {
246        // Converts to `Vec<u8>` without clones or allocation.
247        Self(value.into_vec())
248    }
249}
250
251impl Deref for DynBytes {
252    type Target = [u8];
253
254    fn deref(&self) -> &Self::Target {
255        &self.0
256    }
257}
258
259impl Borrow<[u8]> for DynBytes {
260    fn borrow(&self) -> &[u8] {
261        &self.0
262    }
263}
264
265impl AsRef<[u8]> for DynBytes {
266    fn as_ref(&self) -> &[u8] {
267        &self.0
268    }
269}