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}