use alloy_sol_types::{
abi::{
self,
token::{
DynSeqToken,
FixedSeqToken,
PackedSeqToken,
WordToken,
},
},
private::SolTypeValue,
sol_data,
SolType as AlloySolType,
};
use core::ops::Deref;
use impl_trait_for_tuples::impl_for_tuples;
use ink_prelude::{
borrow::Cow,
boxed::Box,
string::String,
vec::Vec,
};
use itertools::Itertools;
use paste::paste;
use primitive_types::U256;
use crate::types::Address;
#[allow(private_bounds)]
pub trait SolTypeDecode: Sized + private::Sealed {
type AlloyType: AlloySolType;
fn decode(data: &[u8]) -> Result<Self, alloy_sol_types::Error> {
abi::decode::<<Self::AlloyType as AlloySolType>::Token<'_>>(data)
.and_then(Self::detokenize)
}
fn detokenize(
token: <Self::AlloyType as AlloySolType>::Token<'_>,
) -> Result<Self, alloy_sol_types::Error>;
}
#[allow(private_bounds)]
pub trait SolTypeEncode: private::Sealed {
type AlloyType: AlloySolType;
fn encode(&self) -> Vec<u8> {
abi::encode(&self.tokenize())
}
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_>;
}
macro_rules! impl_primitive_decode {
($($ty: ty => $sol_ty: ty),+ $(,)*) => {
$(
impl SolTypeDecode for $ty {
type AlloyType = $sol_ty;
fn detokenize(token: <Self::AlloyType as AlloySolType>::Token<'_>) -> Result<Self, alloy_sol_types::Error> {
Ok(<Self::AlloyType as AlloySolType>::detokenize(token))
}
}
)*
};
}
macro_rules! impl_primitive_encode {
($($ty: ty => $sol_ty: ty),+ $(,)*) => {
$(
impl SolTypeEncode for $ty where Self: SolTypeValue<$sol_ty> {
type AlloyType = $sol_ty;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
<Self::AlloyType as AlloySolType>::tokenize(self)
}
}
)*
};
}
macro_rules! impl_primitive {
($($ty: ty => $sol_ty: ty),+ $(,)*) => {
$(
impl_primitive_decode!($ty => $sol_ty);
impl_primitive_encode!($ty => $sol_ty);
impl private::Sealed for $ty {}
)*
};
}
macro_rules! impl_native_int {
($($bits: literal),+$(,)*) => {
$(
impl_primitive! {
paste!([<i $bits>]) => sol_data::Int<$bits>,
paste!([<u $bits>]) => sol_data::Uint<$bits>,
}
)*
};
}
impl_native_int!(8, 16, 32, 64, 128);
impl_primitive! {
bool => sol_data::Bool,
String => sol_data::String,
}
impl SolTypeDecode for Box<str> {
type AlloyType = sol_data::String;
fn detokenize(
token: <Self::AlloyType as AlloySolType>::Token<'_>,
) -> Result<Self, alloy_sol_types::Error> {
Ok(Box::from(core::str::from_utf8(token.0).map_err(|_| {
alloy_sol_types::Error::type_check_fail(token.0, "string")
})?))
}
}
impl SolTypeEncode for Box<str> {
type AlloyType = sol_data::String;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
PackedSeqToken(self.as_bytes())
}
}
impl private::Sealed for Box<str> {}
impl SolTypeDecode for Address {
type AlloyType = sol_data::Address;
fn detokenize(
token: <Self::AlloyType as AlloySolType>::Token<'_>,
) -> Result<Self, alloy_sol_types::Error> {
Ok(Address::from_slice(&token.0 .0[12..]))
}
}
impl SolTypeEncode for Address {
type AlloyType = sol_data::Address;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
let mut word = [0; 32];
word[12..].copy_from_slice(self.0.as_slice());
WordToken::from(word)
}
}
impl private::Sealed for Address {}
impl SolTypeDecode for U256 {
type AlloyType = sol_data::Uint<256>;
fn detokenize(
token: <Self::AlloyType as AlloySolType>::Token<'_>,
) -> Result<Self, alloy_sol_types::Error> {
Ok(U256::from_big_endian(token.0 .0.as_slice()))
}
}
impl SolTypeEncode for U256 {
type AlloyType = sol_data::Uint<256>;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
WordToken::from(self.to_big_endian())
}
}
impl private::Sealed for U256 {}
impl<T: SolTypeDecode, const N: usize> SolTypeDecode for [T; N] {
type AlloyType = sol_data::FixedArray<T::AlloyType, N>;
fn detokenize(
token: <Self::AlloyType as AlloySolType>::Token<'_>,
) -> Result<Self, alloy_sol_types::Error> {
token
.0
.into_iter()
.map(<T as SolTypeDecode>::detokenize)
.process_results(|iter| iter.collect_array())?
.ok_or_else(|| {
alloy_sol_types::Error::custom(ink_prelude::format!(
"ABI decoding failed: {}",
Self::AlloyType::SOL_NAME
))
})
}
}
impl<T: SolTypeEncode, const N: usize> SolTypeEncode for [T; N] {
type AlloyType = sol_data::FixedArray<T::AlloyType, N>;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
FixedSeqToken(core::array::from_fn(|i| {
<T as SolTypeEncode>::tokenize(&self[i])
}))
}
}
impl<T: private::Sealed, const N: usize> private::Sealed for [T; N] {}
impl<T: SolTypeDecode> SolTypeDecode for Vec<T> {
type AlloyType = sol_data::Array<T::AlloyType>;
fn detokenize(
token: <Self::AlloyType as AlloySolType>::Token<'_>,
) -> Result<Self, alloy_sol_types::Error> {
token
.0
.into_iter()
.map(<T as SolTypeDecode>::detokenize)
.collect()
}
}
impl<T: SolTypeEncode> SolTypeEncode for Vec<T> {
type AlloyType = sol_data::Array<T::AlloyType>;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
DynSeqToken(self.iter().map(<T as SolTypeEncode>::tokenize).collect())
}
}
impl<T: private::Sealed> private::Sealed for Vec<T> {}
impl<T: SolTypeDecode> SolTypeDecode for Box<[T]> {
type AlloyType = sol_data::Array<T::AlloyType>;
fn detokenize(
token: <Self::AlloyType as AlloySolType>::Token<'_>,
) -> Result<Self, alloy_sol_types::Error> {
token
.0
.into_iter()
.map(<T as SolTypeDecode>::detokenize)
.collect()
}
}
impl<T: SolTypeEncode> SolTypeEncode for Box<[T]> {
type AlloyType = sol_data::Array<T::AlloyType>;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
DynSeqToken(self.iter().map(<T as SolTypeEncode>::tokenize).collect())
}
}
impl<T: private::Sealed> private::Sealed for Box<[T]> {}
#[impl_for_tuples(12)]
impl SolTypeDecode for Tuple {
for_tuples!( type AlloyType = ( #( Tuple::AlloyType ),* ); );
#[allow(clippy::unused_unit)]
fn detokenize(
token: <Self::AlloyType as AlloySolType>::Token<'_>,
) -> Result<Self, alloy_sol_types::Error> {
Ok(for_tuples! { ( #( <Tuple as SolTypeDecode>::detokenize(token.Tuple)? ),* ) })
}
}
#[impl_for_tuples(12)]
impl SolTypeEncode for Tuple {
for_tuples!( type AlloyType = ( #( Tuple::AlloyType ),* ); );
#[allow(clippy::unused_unit)]
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
for_tuples!( ( #( <Tuple as SolTypeEncode>::tokenize(&self.Tuple) ),* ) );
}
}
#[impl_for_tuples(12)]
impl private::Sealed for Tuple {}
macro_rules! impl_refs_encode {
($([$($gen:tt)*] $ty: ty), +$(,)*) => {
$(
impl<$($gen)* T: SolTypeEncode> SolTypeEncode for $ty {
type AlloyType = T::AlloyType;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
<T as SolTypeEncode>::tokenize(self)
}
}
impl<$($gen)* T: private::Sealed> private::Sealed for $ty {}
)*
};
}
impl_refs_encode! {
['a,] &'a T,
['a,] &'a mut T,
[] Box<T>,
}
impl<T: SolTypeEncode + Clone> SolTypeEncode for Cow<'_, T> {
type AlloyType = T::AlloyType;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
<T as SolTypeEncode>::tokenize(self.deref())
}
}
impl<T: private::Sealed + Clone> private::Sealed for Cow<'_, T> {}
macro_rules! impl_str_ref_encode {
($($ty: ty),+ $(,)*) => {
$(
impl SolTypeEncode for $ty {
type AlloyType = sol_data::String;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
PackedSeqToken(self.as_bytes())
}
}
impl private::Sealed for $ty {}
)*
};
}
impl_str_ref_encode!(&str, &mut str);
macro_rules! impl_slice_ref_encode {
($($ty: ty),+ $(,)*) => {
$(
impl<T: SolTypeEncode> SolTypeEncode for $ty {
type AlloyType = sol_data::Array<T::AlloyType>;
fn tokenize(&self) -> <Self::AlloyType as AlloySolType>::Token<'_> {
DynSeqToken(self.iter().map(<T as SolTypeEncode>::tokenize).collect())
}
}
impl<T: private::Sealed> private::Sealed for $ty {}
)*
};
}
impl_slice_ref_encode!(&[T], &mut [T]);
pub(super) mod private {
pub trait Sealed {}
}