ink_primitives/
sol.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
15//! Abstractions for implementing Solidity ABI encoding/decoding for arbitrary Rust types.
16
17mod bytes;
18mod types;
19
20#[cfg(test)]
21mod tests;
22
23use core::ops::Deref;
24
25use alloy_sol_types::{
26    sol_data,
27    SolType as AlloySolType,
28};
29use impl_trait_for_tuples::impl_for_tuples;
30use ink_prelude::{
31    borrow::Cow,
32    boxed::Box,
33    string::String,
34    vec::Vec,
35};
36use primitive_types::{
37    H256,
38    U256,
39};
40
41pub use self::{
42    bytes::SolBytes,
43    types::{
44        SolTypeDecode,
45        SolTypeEncode,
46    },
47};
48pub use alloy_sol_types::Error;
49
50use crate::types::{
51    AccountId,
52    Address,
53    Hash,
54};
55
56/// Maps an arbitrary Rust/ink! type to a Solidity ABI type equivalent for Solidity
57/// ABI decoding.
58///
59/// # Note
60///
61/// Implementing this trait entails:
62/// - Declaring the equivalent Solidity ABI type via the `SolType` associated type. See
63///   the [docs for sealed `SolTypeDecode` trait][SolTypeDecode] for a table of Rust/ink!
64///   primitive types mapped to their equivalent Solidity ABI type.
65/// - Implementing the `from_sol_type` method which defines how to convert from the
66///   Solidity ABI representation (i.e. `Self::SolType`) to this type.
67///
68/// # Example
69///
70/// ```
71/// use ink_primitives::SolDecode;
72///
73/// // Example arbitrary type.
74/// struct MyType {
75///     size: u8,
76///     status: bool,
77/// }
78///
79/// // `SolDecode` implementation/mapping.
80/// impl SolDecode for MyType {
81///     type SolType = (u8, bool);
82///
83///     fn from_sol_type(value: Self::SolType) -> Self {
84///         Self {
85///             size: value.0,
86///             status: value.1,
87///         }
88///     }
89/// }
90/// ```
91pub trait SolDecode {
92    /// Equivalent Solidity ABI type representation.
93    type SolType: SolTypeDecode;
94
95    /// Name of equivalent Solidity ABI type.
96    const SOL_NAME: &'static str =
97        <<Self::SolType as SolTypeDecode>::AlloyType as AlloySolType>::SOL_NAME;
98
99    /// Solidity ABI decode into this type.
100    fn decode(data: &[u8]) -> Result<Self, alloy_sol_types::Error>
101    where
102        Self: Sized,
103    {
104        <Self::SolType as SolTypeDecode>::decode(data).map(Self::from_sol_type)
105    }
106
107    /// Converts to `Self` from `Self::SolType`.
108    fn from_sol_type(value: Self::SolType) -> Self;
109}
110
111/// Maps an arbitrary Rust/ink! type to a Solidity ABI type equivalent for Solidity
112/// ABI encoding.
113///
114/// # Note
115///
116/// Implementing this trait entails:
117/// - Declaring the equivalent Solidity ABI type via the `SolType` associated type. See
118///   the [docs for sealed `SolTypeEncode` trait][SolTypeEncode] for a table of Rust/ink!
119///   primitive types mapped to their equivalent Solidity ABI type.
120/// - Implementing the `to_sol_type` method which defines how to convert (preferably via a
121///   borrow) from `&self` to `&Self::SolType` (i.e. the Solidity ABI representation).
122///
123/// # Example
124///
125/// ```
126/// use ink_primitives::SolEncode;
127///
128/// // Example arbitrary type.
129/// struct MyType {
130///     size: u8,
131///     status: bool,
132/// }
133///
134/// // `SolEncode` implementation/mapping.
135/// impl<'a> SolEncode<'a> for MyType {
136///     // NOTE: Prefer reference based representation for better performance.
137///     type SolType = (&'a u8, &'a bool);
138///
139///     fn to_sol_type(&'a self) -> Self::SolType {
140///         (&self.size, &self.status)
141///     }
142/// }
143/// ```
144pub trait SolEncode<'a> {
145    /// Equivalent Solidity ABI type representation.
146    type SolType: SolTypeEncode;
147
148    /// Name of equivalent Solidity ABI type.
149    const SOL_NAME: &'static str =
150        <<Self::SolType as SolTypeEncode>::AlloyType as AlloySolType>::SOL_NAME;
151
152    /// Solidity ABI encode the value.
153    fn encode(&'a self) -> Vec<u8> {
154        <Self::SolType as SolTypeEncode>::encode(&self.to_sol_type())
155    }
156
157    /// Converts from `Self` to `Self::SolType` via either a borrow (if possible), or
158    /// a possibly expensive conversion otherwise.
159    fn to_sol_type(&'a self) -> Self::SolType;
160}
161
162macro_rules! impl_primitive_decode {
163    ($($ty: ty),+ $(,)*) => {
164        $(
165            impl SolDecode for $ty {
166                type SolType = $ty;
167
168                fn from_sol_type(value: Self::SolType) -> Self {
169                    value
170                }
171            }
172        )*
173    };
174}
175
176macro_rules! impl_primitive_encode {
177    ($($ty: ty),+ $(,)*) => {
178        $(
179            impl<'a> SolEncode<'a> for $ty {
180                type SolType = &'a $ty;
181
182                fn to_sol_type(&'a self) -> Self::SolType {
183                    self
184                }
185            }
186        )*
187    };
188}
189
190macro_rules! impl_primitive {
191    ($($ty: ty),+ $(,)*) => {
192        $(
193            impl_primitive_decode!($ty);
194
195            impl_primitive_encode!($ty);
196        )*
197    };
198}
199
200impl_primitive! {
201    // bool
202    bool,
203    // signed integers
204    i8, i16, i32, i64, i128,
205    // unsigned integers
206    u8, u16, u32, u64, u128, U256,
207    // string
208    String,
209    Box<str>,
210    // address
211    Address,
212}
213
214// Rust array <-> Solidity fixed-sized array (i.e. `T[N]`).
215impl<T: SolDecode, const N: usize> SolDecode for [T; N] {
216    type SolType = [T::SolType; N];
217
218    fn from_sol_type(value: Self::SolType) -> Self {
219        value.map(<T as SolDecode>::from_sol_type)
220    }
221}
222
223impl<'a, T: SolEncode<'a>, const N: usize> SolEncode<'a> for [T; N] {
224    type SolType = [T::SolType; N];
225
226    fn to_sol_type(&'a self) -> Self::SolType {
227        self.each_ref().map(<T as SolEncode>::to_sol_type)
228    }
229}
230
231// Rust `Vec` <-> Solidity dynamic size array (i.e. `T[]`).
232impl<T: SolDecode> SolDecode for Vec<T> {
233    type SolType = Vec<T::SolType>;
234
235    fn from_sol_type(value: Self::SolType) -> Self {
236        value
237            .into_iter()
238            .map(<T as SolDecode>::from_sol_type)
239            .collect()
240    }
241}
242
243impl<'a, T: SolEncode<'a>> SolEncode<'a> for Vec<T> {
244    type SolType = Vec<T::SolType>;
245
246    fn to_sol_type(&'a self) -> Self::SolType {
247        self.iter().map(<T as SolEncode>::to_sol_type).collect()
248    }
249}
250
251// Rust `Box<[T]>` (i.e. boxed slice) <-> Solidity dynamic size array (i.e. `T[]`).
252impl<T: SolDecode> SolDecode for Box<[T]> {
253    type SolType = Box<[T::SolType]>;
254
255    fn from_sol_type(value: Self::SolType) -> Self {
256        // TODO: (@davidsemakula) Switch to method call syntax when edition is 2024
257        // (i.e. `value.into_iter()`).
258        // See <https://doc.rust-lang.org/edition-guide/rust-2024/intoiterator-box-slice.html> for details.
259        Box::from_iter(
260            core::iter::IntoIterator::into_iter(value)
261                .map(<T as SolDecode>::from_sol_type),
262        )
263    }
264}
265
266impl<'a, T: SolEncode<'a>> SolEncode<'a> for Box<[T]> {
267    type SolType = Box<[T::SolType]>;
268
269    fn to_sol_type(&'a self) -> Self::SolType {
270        self.iter().map(<T as SolEncode>::to_sol_type).collect()
271    }
272}
273
274// We follow the Rust standard library's convention of implementing traits for tuples up
275// to twelve items long.
276// Ref: <https://doc.rust-lang.org/std/primitive.tuple.html#trait-implementations>
277#[impl_for_tuples(12)]
278impl SolDecode for Tuple {
279    for_tuples!( type SolType = ( #( Tuple::SolType ),* ); );
280
281    #[allow(clippy::unused_unit)]
282    fn from_sol_type(value: Self::SolType) -> Self {
283        for_tuples!( ( #( Tuple::from_sol_type(value.Tuple) ),* ) );
284    }
285}
286
287#[impl_for_tuples(12)]
288impl<'a> SolEncode<'a> for Tuple {
289    for_tuples!( type SolType = ( #( Tuple::SolType ),* ); );
290
291    #[allow(clippy::unused_unit)]
292    fn to_sol_type(&'a self) -> Self::SolType {
293        for_tuples! ( ( #( self.Tuple.to_sol_type() ),* ) )
294    }
295}
296
297// Implements `SolEncode` for reference types.
298macro_rules! impl_refs_encode {
299    ($([$($gen:tt)*] $ty: ty), +$(,)*) => {
300        $(
301            impl<$($gen)* T: SolEncode<'a>> SolEncode<'a> for $ty {
302                type SolType = T::SolType;
303
304                fn to_sol_type(&'a self) -> Self::SolType {
305                    <T as SolEncode>::to_sol_type(self)
306                }
307            }
308        )*
309    };
310}
311
312impl_refs_encode! {
313    ['a,] &'a T,
314    ['a,] &'a mut T,
315    ['a,] Box<T>,
316}
317
318impl<'a, T: SolEncode<'a> + Clone> SolEncode<'a> for Cow<'a, T> {
319    type SolType = T::SolType;
320
321    fn to_sol_type(&'a self) -> Self::SolType {
322        <T as SolEncode>::to_sol_type(self.deref())
323    }
324}
325
326// Implements `SolEncode` for references to `str` and `[T]` DSTs.
327macro_rules! impl_str_ref_encode {
328    ($($ty: ty),+ $(,)*) => {
329        $(
330            impl<'a> SolEncode<'a> for $ty {
331                type SolType = &'a str;
332
333                fn to_sol_type(&'a self) -> Self::SolType {
334                    self
335                }
336            }
337        )*
338    };
339}
340
341impl_str_ref_encode!(&'a str, &'a mut str);
342
343macro_rules! impl_slice_ref_encode {
344    ($($ty: ty),+ $(,)*) => {
345        $(
346            impl<'a, T: SolEncode<'a>> SolEncode<'a> for $ty {
347                type SolType = Vec<T::SolType>;
348
349                fn to_sol_type(&'a self) -> Self::SolType {
350                    self.iter().map(<T as SolEncode>::to_sol_type).collect()
351                }
352            }
353        )*
354    };
355}
356
357impl_slice_ref_encode!(&'a [T], &'a mut [T]);
358
359// AccountId <-> bytes32
360impl SolDecode for AccountId {
361    type SolType = SolBytes<[u8; 32]>;
362
363    fn from_sol_type(value: Self::SolType) -> Self {
364        AccountId(value.0)
365    }
366}
367
368impl SolEncode<'_> for AccountId {
369    type SolType = SolBytes<[u8; 32]>;
370
371    fn encode(&self) -> Vec<u8> {
372        // Override for better performance.
373        sol_data::FixedBytes::abi_encode(self)
374    }
375
376    fn to_sol_type(&self) -> Self::SolType {
377        // NOTE: Not actually used for encoding because of `encode` override above (for
378        // better performance).
379        // Arbitrary newtype wrappers can achieve similar performance (without overriding
380        // `encode`) by using `SolBytes<[u8; 32]>` as the inner type and returning
381        // `&self.0`.
382        SolBytes(self.0)
383    }
384}
385
386// Hash <-> bytes32
387impl SolDecode for Hash {
388    type SolType = SolBytes<[u8; 32]>;
389
390    fn from_sol_type(value: Self::SolType) -> Self {
391        Hash::from(value.0)
392    }
393}
394
395impl SolEncode<'_> for Hash {
396    type SolType = SolBytes<[u8; 32]>;
397
398    fn encode(&self) -> Vec<u8> {
399        // Override for better performance.
400        sol_data::FixedBytes::abi_encode(self)
401    }
402
403    fn to_sol_type(&self) -> Self::SolType {
404        // NOTE: Not actually used for encoding because of `encode` override above (for
405        // better performance).
406        // Arbitrary newtype wrappers can achieve similar performance (without overriding
407        // `encode`) by using `SolBytes<[u8; 32]>` as the inner type and returning
408        // `&self.0`.
409        SolBytes::<[u8; 32]>((*self).into())
410    }
411}
412
413// H256 <-> bytes32
414impl SolDecode for H256 {
415    type SolType = SolBytes<[u8; 32]>;
416
417    fn from_sol_type(value: Self::SolType) -> Self {
418        H256(value.0)
419    }
420}
421
422impl SolEncode<'_> for H256 {
423    type SolType = SolBytes<[u8; 32]>;
424
425    fn encode(&self) -> Vec<u8> {
426        // Override for better performance.
427        sol_data::FixedBytes::abi_encode(&self.0)
428    }
429
430    fn to_sol_type(&self) -> Self::SolType {
431        // NOTE: Not actually used for encoding because of `encode` override above (for
432        // better performance).
433        // Arbitrary newtype wrappers can achieve similar performance (without overriding
434        // `encode`) by using `SolBytes<[u8; 32]>` as the inner type and returning
435        // `&self.0`.
436        SolBytes(self.0)
437    }
438}