ink_primitives/sol/
types.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    clone::Clone,
17    ops::Deref,
18};
19
20use alloy_sol_types::{
21    abi::{
22        self,
23        token::{
24            PackedSeqToken,
25            WordToken,
26        },
27        Encoder,
28    },
29    sol_data,
30    SolType as AlloySolType,
31};
32use impl_trait_for_tuples::impl_for_tuples;
33use ink_prelude::{
34    borrow::{
35        Cow,
36        ToOwned,
37    },
38    boxed::Box,
39    string::String,
40    vec::Vec,
41};
42use itertools::Itertools;
43use paste::paste;
44use primitive_types::U256;
45
46use crate::{
47    sol::{
48        encodable::{
49            DynSizeDefault,
50            Encodable,
51            FixedSizeDefault,
52            TokenOrDefault,
53        },
54        Error,
55    },
56    types::Address,
57};
58
59/// A Rust/ink! equivalent of a Solidity ABI type that implements logic for Solidity ABI
60/// decoding.
61///
62/// # Rust/ink! to Solidity ABI type mapping
63///
64/// | Rust/ink! type | Solidity ABI type | Notes |
65/// | -------------- | ----------------- | ----- |
66/// | `bool` | `bool` ||
67/// | `iN` for `N ∈ {8,16,32,64,128}` | `intN` | e.g `i8` ↔ `int8` |
68/// | `uN` for `N ∈ {8,16,32,64,128}` | `uintN` | e.g `u8` ↔ `uint8` |
69/// | `U256` | `uint256` ||
70/// | `String` | `string` ||
71/// | `Box<str>` | `string` ||
72/// | `Address` / `H160` | `address` | `Address` is a type alias for the `H160` type used for addresses in `pallet-revive` |
73/// | `[T; N]` for `const N: usize` | `T[N]` | e.g. `[i8; 64]` ↔ `int8[64]` |
74/// | `Vec<T>` | `T[]` | e.g. `Vec<i8>` ↔ `int8[]` |
75/// | `Box<[T]>` | `T[]` | e.g. `Box<[i8]>` ↔ `int8[]` |
76/// | `SolBytes<u8>` | `bytes1` ||
77/// | `SolBytes<[u8; N]>` for `1 <= N <= 32` | `bytesN` | e.g. `SolBytes<[u8; 32]>` ↔ `bytes32` |
78/// | `SolBytes<Vec<u8>>` | `bytes` ||
79/// | `SolBytes<Box<[u8]>>` | `bytes` ||
80/// | `(T1, T2, T3, ... T12)` | `(U1, U2, U3, ... U12)` | where `T1` ↔ `U1`, ... `T12` ↔ `U12` e.g. `(bool, u8, Address)` ↔ `(bool, uint8, address)` |
81///
82/// Ref: <https://docs.soliditylang.org/en/latest/abi-spec.html#types>
83///
84/// # Note
85///
86/// This trait is sealed and cannot be implemented for types outside `ink_primitives`.
87pub trait SolTypeDecode: Sized + private::Sealed {
88    /// Equivalent Solidity ABI type from [`alloy_sol_types`].
89    type AlloyType: AlloySolType;
90
91    /// Solidity ABI decode into this type.
92    fn decode(data: &[u8]) -> Result<Self, Error> {
93        abi::decode::<<Self::AlloyType as AlloySolType>::Token<'_>>(data)
94            .map_err(Error::from)
95            .and_then(Self::detokenize)
96    }
97
98    /// Detokenizes this type's value from the given token.
99    fn detokenize(
100        token: <Self::AlloyType as AlloySolType>::Token<'_>,
101    ) -> Result<Self, Error>;
102}
103
104/// A Rust/ink! equivalent of a Solidity ABI type that implements logic for Solidity ABI
105/// encoding.
106///
107/// # Rust/ink! to Solidity ABI type mapping
108///
109/// | Rust/ink! type | Solidity ABI type | Notes |
110/// | -------------- | ----------------- | ----- |
111/// | `bool` | `bool` ||
112/// | `iN` for `N ∈ {8,16,32,64,128}` | `intN` | e.g `i8` ↔ `int8` |
113/// | `uN` for `N ∈ {8,16,32,64,128}` | `uintN` | e.g `u8` ↔ `uint8` |
114/// | `U256` | `uint256` ||
115/// | `String` | `string` ||
116/// | `Box<str>` | `string` ||
117/// | `Address` / `H160` | `address` | `Address` is a type alias for the `H160` type used for addresses in `pallet-revive` |
118/// | `[T; N]` for `const N: usize` | `T[N]` | e.g. `[i8; 64]` ↔ `int8[64]` |
119/// | `Vec<T>` | `T[]` | e.g. `Vec<i8>` ↔ `int8[]` |
120/// | `Box<[T]>` | `T[]` | e.g. `Box<[i8]>` ↔ `int8[]` |
121/// | `SolBytes<u8>` |  `bytes1` ||
122/// | `SolBytes<[u8; N]>` for `1 <= N <= 32` | `bytesN` | e.g. `SolBytes<[u8; 32]>` ↔ `bytes32` |
123/// | `SolBytes<Vec<u8>>` | `bytes` ||
124/// | `SolBytes<Box<[u8]>>` | `bytes` ||
125/// | `(T1, T2, T3, ... T12)` | `(U1, U2, U3, ... U12)` | where `T1` ↔ `U1`, ... `T12` ↔ `U12` e.g. `(bool, u8, Address)` ↔ `(bool, uint8, address)` |
126/// | `&str`, `&mut str` | `string` ||
127/// | `&T`, `&mut T`, `Box<T>` | `T` | e.g. `&i8 ↔ int8` |
128/// | `&[T]`, `&mut [T]` | `T[]` | e.g. `&[i8]` ↔ `int8[]` |
129///
130/// Ref: <https://docs.soliditylang.org/en/latest/abi-spec.html#types>
131///
132/// # Note
133///
134/// This trait is sealed and cannot be implemented for types outside `ink_primitives`.
135pub trait SolTypeEncode: SolTokenType + private::Sealed {
136    /// Equivalent Solidity ABI type from [`alloy_sol_types`].
137    type AlloyType: AlloySolType;
138
139    /// An encodable representation of the default value for this type.
140    const DEFAULT_VALUE: Self::DefaultType;
141
142    /// Solidity ABI encode the value.
143    fn encode(&self) -> Vec<u8> {
144        let token = self.tokenize();
145        let mut encoder = Encoder::with_capacity(token.total_words());
146        token.encode(&mut encoder);
147        encoder.into_bytes()
148    }
149
150    /// Tokenizes the given value into a [`Self::AlloyType`] token.
151    fn tokenize(&self) -> Self::TokenType<'_>;
152}
153
154/// Describes a "tokenizable" representation of [`SolTypeEncode`] implementing type.
155///
156/// # Note
157///
158/// The `TokenType` representation is similar to alloy types that implement `Token` and
159/// `TokenSeq` traits, but is instead based on local trait [`Encodable`] which allows us
160/// to implement it for custom abstractions that allow for "default" representations of
161/// [`Self::TokenType`], most notably [`TokenOrDefault`].
162//
163// # Design Notes
164//
165// These abstractions are mainly necessary because the return type of
166// [`alloy_sol_types::SolType::tokenize`] is encumbered by the lifetime of it's input.
167//
168// In the case of a "default" representation, this input would be an owned value defined
169// in [`SolTypeEncode::tokenize`], and thus it's lifetime would be too short for the
170// return type of [`SolTypeEncode::tokenize`] when using a `Token<'a>` based return type
171// (i.e. `'a` would be lifetime of `&self`).
172//
173// Static references as solution are too cumbersome because:
174// - `SolTypeEncode` implementing types are composable (i.e. arrays, `Vec`s and tuples of
175//   `T: SolTypeEncode` implement `SolTypeEncode` generically)
176// - Tokenizable default representations of some types are based on alloy types that use
177//   "interior mutability" (e.g. the tokenizable default for `SolBytes<Vec<u8>>` would be
178//   based on `alloy_primitives::bytes::Bytes`)
179// Ref: <https://doc.rust-lang.org/reference/interior-mutability.html>
180//
181// Lastly, this trait only exists separate from `SolTypeEncode` so that the
182// `TokenType<'enc>` GAT (Generic Associated Type) does NOT have a `where Self: 'a` bound
183// which is too limiting for our use case.
184//
185// See <https://github.com/rust-lang/rust/issues/87479> for details.
186pub trait SolTokenType: private::Sealed {
187    /// The type of an encodable representation of this type.
188    type TokenType<'enc>: Encodable;
189
190    /// The type of an encodable "default" representation of this type.
191    type DefaultType: Encodable;
192}
193
194macro_rules! impl_primitive_decode {
195    ($($ty: ty => $sol_ty: ty),+ $(,)*) => {
196        $(
197            impl SolTypeDecode for $ty {
198                type AlloyType = $sol_ty;
199
200                fn detokenize(token: <Self::AlloyType as AlloySolType>::Token<'_>) -> Result<Self, Error> {
201                    Ok(<Self::AlloyType as AlloySolType>::detokenize(token))
202                }
203            }
204        )*
205    };
206}
207
208macro_rules! impl_primitive_encode {
209    ($($ty: ty => ($sol_ty: ty, $default_ty: ty, $default_value: expr)),+ $(,)*) => {
210        $(
211            impl SolTypeEncode for $ty {
212                type AlloyType = $sol_ty;
213
214                const DEFAULT_VALUE: Self::DefaultType = $default_value;
215
216                fn tokenize(&self) -> Self::TokenType<'_> {
217                    <Self::AlloyType as AlloySolType>::tokenize(self)
218                }
219            }
220
221            impl SolTokenType for $ty {
222                type TokenType<'enc> = <$sol_ty as AlloySolType>::Token<'enc>;
223
224                type DefaultType = $default_ty;
225            }
226        )*
227    };
228}
229
230macro_rules! impl_primitive {
231    ($($ty: ty => ($sol_ty: ty, $default_ty: ty, $default_value: expr)),+ $(,)*) => {
232        $(
233            impl_primitive_decode!($ty => $sol_ty);
234
235            impl_primitive_encode!($ty => ($sol_ty, $default_ty, $default_value));
236
237            impl private::Sealed for $ty {}
238        )*
239    };
240}
241
242macro_rules! impl_native_int {
243    ($($bits: literal),+$(,)*) => {
244        $(
245            impl_primitive! {
246                // signed
247                paste!([<i $bits>]) => (sol_data::Int<$bits>, FixedSizeDefault, FixedSizeDefault::WORD),
248                // unsigned
249                paste!([<u $bits>]) => (sol_data::Uint<$bits>, FixedSizeDefault, FixedSizeDefault::WORD),
250            }
251        )*
252    };
253}
254
255impl_native_int!(8, 16, 32, 64, 128);
256
257impl_primitive! {
258    // bool
259    bool => (sol_data::Bool, FixedSizeDefault, FixedSizeDefault::WORD),
260    // string
261    String => (sol_data::String, DynSizeDefault, DynSizeDefault),
262}
263
264// Rust `Box<str>` (i.e. boxed string slice) <-> Solidity `string`.
265impl SolTypeDecode for Box<str> {
266    type AlloyType = sol_data::String;
267
268    fn detokenize(
269        token: <Self::AlloyType as AlloySolType>::Token<'_>,
270    ) -> Result<Self, Error> {
271        Ok(Box::from(core::str::from_utf8(token.0).map_err(|_| Error)?))
272    }
273}
274
275impl SolTypeEncode for Box<str> {
276    type AlloyType = sol_data::String;
277
278    const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
279
280    fn tokenize(&self) -> Self::TokenType<'_> {
281        PackedSeqToken(self.as_bytes())
282    }
283}
284
285impl SolTokenType for Box<str> {
286    type TokenType<'enc> = PackedSeqToken<'enc>;
287
288    type DefaultType = DynSizeDefault;
289}
290
291impl private::Sealed for Box<str> {}
292
293// Address <-> address
294impl SolTypeDecode for Address {
295    type AlloyType = sol_data::Address;
296
297    fn detokenize(
298        token: <Self::AlloyType as AlloySolType>::Token<'_>,
299    ) -> Result<Self, Error> {
300        // We skip the conversion to `alloy_sol_types::private::Address` which will end up
301        // just taking the last 20 bytes of `alloy_sol_types::abi::token::WordToken` as
302        // well anyway.
303        // Ref: <https://github.com/alloy-rs/core/blob/5ae4fe0b246239602c97cc5a2f2e4bc780e2024a/crates/primitives/src/bits/address.rs#L132-L134>
304        Ok(Address::from_slice(&token.0 .0[12..]))
305    }
306}
307
308impl SolTypeEncode for Address {
309    type AlloyType = sol_data::Address;
310
311    const DEFAULT_VALUE: Self::DefaultType = FixedSizeDefault::WORD;
312
313    fn tokenize(&self) -> Self::TokenType<'_> {
314        // We skip the conversion to `alloy_sol_types::private::Address` which will just
315        // end up doing the conversion below anyway.
316        // Ref: <https://github.com/alloy-rs/core/blob/5ae4fe0b246239602c97cc5a2f2e4bc780e2024a/crates/primitives/src/bits/address.rs#L149-L153>
317        let mut word = [0; 32];
318        word[12..].copy_from_slice(self.0.as_slice());
319        WordToken::from(word)
320    }
321}
322
323impl SolTokenType for Address {
324    type TokenType<'enc> = WordToken;
325
326    type DefaultType = FixedSizeDefault;
327}
328
329impl private::Sealed for Address {}
330
331// U256 <-> uint256
332impl SolTypeDecode for U256 {
333    type AlloyType = sol_data::Uint<256>;
334
335    fn detokenize(
336        token: <Self::AlloyType as AlloySolType>::Token<'_>,
337    ) -> Result<Self, Error> {
338        Ok(U256::from_big_endian(token.0 .0.as_slice()))
339    }
340}
341
342impl SolTypeEncode for U256 {
343    type AlloyType = sol_data::Uint<256>;
344
345    const DEFAULT_VALUE: Self::DefaultType = FixedSizeDefault::WORD;
346
347    fn tokenize(&self) -> Self::TokenType<'_> {
348        // `<Self::AlloyType as AlloySolType>::tokenize(self)` won't work because
349        // `primitive_types::U256` does NOT implement
350        // `Borrow<alloy_sol_types::private::U256>`. And both the `U256` and
351        // `Borrow` are foreign, so we can't just implement it.
352        WordToken::from(self.to_big_endian())
353    }
354}
355
356impl SolTokenType for U256 {
357    type TokenType<'enc> = WordToken;
358
359    type DefaultType = FixedSizeDefault;
360}
361
362impl private::Sealed for U256 {}
363
364// Rust array <-> Solidity fixed-sized array (i.e. `T[N]`).
365impl<T: SolTypeDecode, const N: usize> SolTypeDecode for [T; N] {
366    type AlloyType = sol_data::FixedArray<T::AlloyType, N>;
367
368    fn detokenize(
369        token: <Self::AlloyType as AlloySolType>::Token<'_>,
370    ) -> Result<Self, Error> {
371        // Takes advantage of optimized `SolTypeDecode::detokenize` implementations and
372        // skips unnecessary conversions to `T::AlloyType::RustType`.
373        // FIXME: (@davidsemakula) replace with `array::try_map` if it's ever stabilized.
374        // Ref: <https://github.com/rust-lang/rust/issues/79711>
375        // Ref: <https://doc.rust-lang.org/nightly/std/primitive.array.html#method.try_map>
376        token
377            .0
378            .into_iter()
379            .map(<T as SolTypeDecode>::detokenize)
380            .process_results(|iter| iter.collect_array())?
381            .ok_or(Error)
382    }
383}
384
385impl<T: SolTypeEncode, const N: usize> SolTypeEncode for [T; N] {
386    type AlloyType = sol_data::FixedArray<T::AlloyType, N>;
387
388    const DEFAULT_VALUE: Self::DefaultType = [T::DEFAULT_VALUE; N];
389
390    fn tokenize(&self) -> Self::TokenType<'_> {
391        // Does NOT require `SolTypeValue<Self::AlloyType>` and instead relies on
392        // `SolTypeEncode::tokenize`.
393        core::array::from_fn(|i| <T as SolTypeEncode>::tokenize(&self[i]))
394    }
395}
396
397impl<T: SolTokenType, const N: usize> SolTokenType for [T; N] {
398    type TokenType<'enc> = [T::TokenType<'enc>; N];
399
400    type DefaultType = [T::DefaultType; N];
401}
402
403impl<T: private::Sealed, const N: usize> private::Sealed for [T; N] {}
404
405// Rust `Vec` <-> Solidity dynamic size array (i.e. `T[]`).
406impl<T: SolTypeDecode> SolTypeDecode for Vec<T> {
407    type AlloyType = sol_data::Array<T::AlloyType>;
408
409    fn detokenize(
410        token: <Self::AlloyType as AlloySolType>::Token<'_>,
411    ) -> Result<Self, Error> {
412        // Takes advantage of optimized `SolTypeDecode::detokenize` implementations and
413        // skips unnecessary conversions to `T::AlloyType::RustType`.
414        token
415            .0
416            .into_iter()
417            .map(<T as SolTypeDecode>::detokenize)
418            .collect()
419    }
420}
421
422impl<T: SolTypeEncode> SolTypeEncode for Vec<T> {
423    type AlloyType = sol_data::Array<T::AlloyType>;
424
425    const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
426
427    fn tokenize(&self) -> Self::TokenType<'_> {
428        // Does NOT require `SolTypeValue<Self::AlloyType>` and instead relies on
429        // `SolTypeEncode::tokenize`.
430        self.iter().map(<T as SolTypeEncode>::tokenize).collect()
431    }
432}
433
434impl<T: SolTokenType> SolTokenType for Vec<T> {
435    type TokenType<'enc> = Vec<T::TokenType<'enc>>;
436
437    type DefaultType = DynSizeDefault;
438}
439
440impl<T: private::Sealed> private::Sealed for Vec<T> {}
441
442// Rust `Box<[T]>` (i.e. boxed slice) <-> Solidity dynamic size array (i.e. `T[]`).
443impl<T: SolTypeDecode> SolTypeDecode for Box<[T]> {
444    type AlloyType = sol_data::Array<T::AlloyType>;
445
446    fn detokenize(
447        token: <Self::AlloyType as AlloySolType>::Token<'_>,
448    ) -> Result<Self, Error> {
449        // Takes advantage of optimized `SolTypeDecode::detokenize` implementations and
450        // skips unnecessary conversions to `T::AlloyType::RustType`.
451        token
452            .0
453            .into_iter()
454            .map(<T as SolTypeDecode>::detokenize)
455            .collect()
456    }
457}
458
459impl<T: SolTypeEncode> SolTypeEncode for Box<[T]> {
460    type AlloyType = sol_data::Array<T::AlloyType>;
461
462    const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
463
464    fn tokenize(&self) -> Self::TokenType<'_> {
465        // Does NOT require `SolTypeValue<Self::AlloyType>` and instead relies on
466        // `SolTypeEncode::tokenize`.
467        self.iter().map(<T as SolTypeEncode>::tokenize).collect()
468    }
469}
470
471impl<T: SolTokenType> SolTokenType for Box<[T]> {
472    type TokenType<'enc> = Vec<T::TokenType<'enc>>;
473
474    type DefaultType = DynSizeDefault;
475}
476
477impl<T: private::Sealed> private::Sealed for Box<[T]> {}
478
479// Rust `Cow<'_, [T]>` (i.e. clone-on-write slice) <-> Solidity dynamic size array (i.e.
480// `T[]`).
481impl<T> SolTypeDecode for Cow<'_, [T]>
482where
483    T: SolTypeDecode + Clone,
484    [T]: ToOwned,
485{
486    type AlloyType = sol_data::Array<T::AlloyType>;
487
488    fn detokenize(
489        token: <Self::AlloyType as AlloySolType>::Token<'_>,
490    ) -> Result<Self, Error> {
491        // Takes advantage of optimized `SolTypeDecode::detokenize` implementations and
492        // skips unnecessary conversions to `T::AlloyType::RustType`.
493        token
494            .0
495            .into_iter()
496            .map(<T as SolTypeDecode>::detokenize)
497            .collect()
498    }
499}
500
501impl<T> SolTypeEncode for Cow<'_, [T]>
502where
503    T: SolTypeEncode + Clone,
504    [T]: ToOwned,
505{
506    type AlloyType = sol_data::Array<T::AlloyType>;
507
508    const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
509
510    fn tokenize(&self) -> Self::TokenType<'_> {
511        // Does NOT require `SolTypeValue<Self::AlloyType>` and instead relies on
512        // `SolTypeEncode::tokenize`.
513        self.iter().map(<T as SolTypeEncode>::tokenize).collect()
514    }
515}
516
517impl<T> SolTokenType for Cow<'_, [T]>
518where
519    T: SolTokenType + Clone,
520    [T]: ToOwned,
521{
522    type TokenType<'enc> = Vec<T::TokenType<'enc>>;
523
524    type DefaultType = DynSizeDefault;
525}
526
527impl<T: private::Sealed> private::Sealed for Cow<'_, [T]> where [T]: ToOwned {}
528
529// We follow the Rust standard library's convention of implementing traits for tuples up
530// to twelve items long.
531// Ref: <https://doc.rust-lang.org/std/primitive.tuple.html#trait-implementations>
532#[impl_for_tuples(12)]
533impl SolTypeDecode for Tuple {
534    for_tuples!( type AlloyType = ( #( Tuple::AlloyType ),* ); );
535
536    #[allow(clippy::unused_unit)]
537    fn detokenize(
538        token: <Self::AlloyType as AlloySolType>::Token<'_>,
539    ) -> Result<Self, Error> {
540        // Takes advantage of optimized `SolTypeDecode::detokenize` implementations and
541        // skips unnecessary conversions to `T::AlloyType::RustType`.
542        Ok(for_tuples! { ( #( <Tuple as SolTypeDecode>::detokenize(token.Tuple)? ),* ) })
543    }
544}
545
546#[impl_for_tuples(12)]
547impl SolTypeEncode for Tuple {
548    for_tuples!( type AlloyType = ( #( Tuple::AlloyType ),* ); );
549
550    const DEFAULT_VALUE: Self::DefaultType =
551        (for_tuples! { #( Tuple::DEFAULT_VALUE ),* });
552
553    #[allow(clippy::unused_unit)]
554    fn tokenize(&self) -> Self::TokenType<'_> {
555        // Does NOT require `SolTypeValue<Self::AlloyType>` and instead relies on
556        // `SolTypeEncode::tokenize`.
557        for_tuples!( ( #( <Tuple as SolTypeEncode>::tokenize(&self.Tuple) ),* ) );
558    }
559}
560
561// `impl-trait-for-tuples` doesn't support GATs yet, so we fallback to a declarative macro
562// for `SolTokenType`.
563//
564// Ref: <https://github.com/bkchr/impl-trait-for-tuples/issues/11>
565macro_rules! impl_sol_token_type {
566    ( $($ty: ident),* ) => {
567        impl<$( $ty: SolTokenType, )*> SolTokenType for ( $( $ty, )* ) {
568            type TokenType<'enc> = ( $( $ty ::TokenType<'enc>, )* );
569
570            type DefaultType = ( $( $ty ::DefaultType, )* );
571        }
572    };
573}
574
575impl_all_tuples!(impl_sol_token_type);
576
577#[impl_for_tuples(12)]
578impl private::Sealed for Tuple {}
579
580// Rust `Option<T>` <-> Solidity `(bool, T)`.
581impl<T: SolTypeDecode> SolTypeDecode for Option<T> {
582    type AlloyType = (sol_data::Bool, T::AlloyType);
583
584    fn detokenize(
585        token: <Self::AlloyType as AlloySolType>::Token<'_>,
586    ) -> Result<Self, Error> {
587        let flag = bool::detokenize(token.0)?;
588        let value = T::detokenize(token.1)?;
589        Ok(if flag { Some(value) } else { None })
590    }
591}
592
593impl<T> SolTypeEncode for Option<T>
594where
595    T: SolTypeEncode,
596{
597    type AlloyType = (sol_data::Bool, T::AlloyType);
598
599    const DEFAULT_VALUE: Self::DefaultType = (FixedSizeDefault::WORD, T::DEFAULT_VALUE);
600
601    fn tokenize(&self) -> Self::TokenType<'_> {
602        match self {
603            Some(value) => (true.tokenize(), TokenOrDefault::Token(value.tokenize())),
604            None => (false.tokenize(), TokenOrDefault::Default(T::DEFAULT_VALUE)),
605        }
606    }
607}
608
609impl<T: SolTokenType> SolTokenType for Option<T> {
610    type TokenType<'enc> = (
611        WordToken,
612        TokenOrDefault<T::TokenType<'enc>, T::DefaultType>,
613    );
614
615    type DefaultType = (FixedSizeDefault, T::DefaultType);
616}
617
618impl<T: private::Sealed> private::Sealed for Option<T> {}
619
620// Implements `SolTypeEncode` for reference types.
621macro_rules! impl_refs_encode {
622    ($($ty: ty), +$(,)*) => {
623        $(
624            impl<T: SolTypeEncode> SolTypeEncode for $ty {
625                type AlloyType = T::AlloyType;
626
627                const DEFAULT_VALUE: Self::DefaultType = T::DEFAULT_VALUE;
628
629                fn tokenize(&self) -> Self::TokenType<'_> {
630                    <T as SolTypeEncode>::tokenize(self)
631                }
632            }
633
634            impl<T: SolTokenType> SolTokenType for $ty {
635                type TokenType<'enc> = T::TokenType<'enc>;
636
637                type DefaultType = T::DefaultType;
638            }
639
640            impl<T: private::Sealed> private::Sealed for $ty {}
641        )*
642    };
643}
644
645impl_refs_encode! {
646    &T,
647    &mut T,
648    Box<T>,
649}
650
651// Implements `SolTypeEncode` for smart pointers.
652impl<T: SolTypeEncode + Clone> SolTypeEncode for Cow<'_, T> {
653    type AlloyType = T::AlloyType;
654
655    const DEFAULT_VALUE: Self::DefaultType = T::DEFAULT_VALUE;
656
657    fn tokenize(&self) -> Self::TokenType<'_> {
658        <T as SolTypeEncode>::tokenize(self.deref())
659    }
660}
661
662impl<T: SolTokenType + Clone> SolTokenType for Cow<'_, T> {
663    type TokenType<'enc> = T::TokenType<'enc>;
664
665    type DefaultType = T::DefaultType;
666}
667
668impl<T: private::Sealed + Clone> private::Sealed for Cow<'_, T> {}
669
670// Implements `SolTypeEncode` for references and smart pointers to `str`.
671macro_rules! impl_str_ref_encode {
672    ($($ty: ty),+ $(,)*) => {
673        $(
674            impl SolTypeEncode for $ty {
675                type AlloyType = sol_data::String;
676
677                const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
678
679                fn tokenize(&self) -> Self::TokenType<'_> {
680                    PackedSeqToken(self.as_bytes())
681                }
682            }
683
684            impl SolTokenType for $ty {
685                type TokenType<'enc> = PackedSeqToken<'enc>;
686
687                type DefaultType = DynSizeDefault;
688            }
689
690            impl private::Sealed for $ty {}
691        )*
692    };
693}
694
695impl_str_ref_encode!(&str, &mut str, Cow<'_, str>);
696
697// Implements `SolTypeEncode` for references to `[T]` DSTs.
698macro_rules! impl_slice_ref_encode {
699    ($($ty: ty),+ $(,)*) => {
700        $(
701            impl<T: SolTypeEncode> SolTypeEncode for $ty {
702                type AlloyType = sol_data::Array<T::AlloyType>;
703
704                const DEFAULT_VALUE: Self::DefaultType = DynSizeDefault;
705
706                fn tokenize(&self) -> Self::TokenType<'_> {
707                    // Does NOT require `SolTypeValue<Self::AlloyType>` and instead relies on
708                    // `SolTypeEncode::tokenize`.
709                    self.iter().map(<T as SolTypeEncode>::tokenize).collect()
710                }
711            }
712
713            impl<T: SolTokenType> SolTokenType for $ty {
714                type TokenType<'enc> = Vec<T::TokenType<'enc>>;
715
716                type DefaultType = DynSizeDefault;
717            }
718
719            impl<T: private::Sealed> private::Sealed for $ty {}
720        )*
721    };
722}
723
724impl_slice_ref_encode!(&[T], &mut [T]);
725
726pub(super) mod private {
727    /// Seals implementations of `SolTypeEncode` and `SolTypeDecode`.
728    pub trait Sealed {}
729}