ink_primitives/sol/
params.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 alloy_sol_types::{
16    SolType as AlloySolType,
17    abi,
18};
19use impl_trait_for_tuples::impl_for_tuples;
20use ink_prelude::vec::Vec;
21
22use super::{
23    Error,
24    SolDecode,
25    SolEncode,
26    SolTypeDecode,
27    SolTypeEncode,
28    encodable::{
29        Encodable,
30        EncodableParams,
31    },
32    encoder::Encoder,
33    types::SolTokenType,
34};
35
36/// Solidity ABI decode from parameter data (e.g. function, event or error parameters).
37///
38/// # Note
39///
40/// This trait is sealed and cannot be implemented for types outside `ink_primitives`.
41pub trait SolParamsDecode: SolDecode + Sized + private::Sealed {
42    /// Name of equivalent Solidity ABI type.
43    const SOL_NAME: &'static str = <Self as SolDecode>::SOL_NAME;
44
45    /// Solidity ABI decode parameter data into this type.
46    fn decode(data: &[u8]) -> Result<Self, Error>;
47}
48
49/// Solidity ABI encode as a parameter sequence (e.g. function, event or error
50/// parameters).
51///
52/// # Note
53///
54/// This trait is sealed and cannot be implemented for types outside `ink_primitives`.
55pub trait SolParamsEncode<'a>: SolEncode<'a> + private::Sealed {
56    /// Name of equivalent Solidity ABI type.
57    const SOL_NAME: &'static str = <Self as SolEncode<'a>>::SOL_NAME;
58
59    /// Solidity ABI encode the value as a parameter sequence.
60    fn encode(&'a self) -> Vec<u8>;
61
62    /// Solidity ABI encode the value into the given buffer as a parameter sequence, and
63    /// returns the number of bytes written.
64    fn encode_to(&'a self, buffer: &mut [u8]) -> usize;
65}
66
67// We follow the Rust standard library's convention of implementing traits for tuples up
68// to twelve items long.
69// Ref: <https://doc.rust-lang.org/std/primitive.tuple.html#trait-implementations>
70#[impl_for_tuples(1, 12)]
71#[tuple_types_custom_trait_bound(SolDecode)]
72impl SolParamsDecode for Tuple {
73    fn decode(data: &[u8]) -> Result<Self, Error> {
74        abi::decode_params::<
75            <<<Self as SolDecode>::SolType as SolTypeDecode>::AlloyType as AlloySolType>::Token<'_>,
76        >(data)
77            .map_err(Error::from)
78            .and_then(<<Self as SolDecode>::SolType as SolTypeDecode>::detokenize)
79            .and_then(<Self as SolDecode>::from_sol_type)
80    }
81}
82
83#[impl_for_tuples(1, 12)]
84#[tuple_types_custom_trait_bound(SolEncode<'a>)]
85impl<'a> SolParamsEncode<'a> for Tuple {
86    #[inline]
87    fn encode(&'a self) -> Vec<u8> {
88        SolTypeParamsEncode::encode(&self.to_sol_type())
89    }
90
91    #[inline]
92    fn encode_to(&'a self, buffer: &mut [u8]) -> usize {
93        SolTypeParamsEncode::encode_to(&self.to_sol_type(), buffer)
94    }
95}
96
97// Optimized implementations for unit (i.e. `()`).
98impl SolParamsDecode for () {
99    fn decode(_: &[u8]) -> Result<Self, Error> {
100        // NOTE: Solidity ABI decoding doesn't validate input length.
101        Ok(())
102    }
103}
104
105impl SolParamsEncode<'_> for () {
106    fn encode(&self) -> Vec<u8> {
107        Vec::new()
108    }
109
110    fn encode_to(&self, _: &mut [u8]) -> usize {
111        0
112    }
113}
114
115#[impl_for_tuples(12)]
116#[tuple_types_no_default_trait_bound]
117impl private::Sealed for Tuple {}
118
119/// Same as `SolParamsEncode` but with a `SolTypeEncode` instead of `SolEncode` bound.
120///
121/// # Note
122///
123/// This trait is sealed and cannot be implemented for types outside `ink_primitives`.
124//
125// Design Notes
126//
127// This trait is useful for cases where we want to skip the conversion from `SolEncode` to
128// `SolTypeEncode`.
129#[doc(hidden)]
130pub trait SolTypeParamsEncode: SolTypeEncode + private::Sealed {
131    /// Solidity ABI encode the value as a parameter sequence.
132    fn encode(&self) -> Vec<u8>;
133
134    /// Solidity ABI encode the value into the given buffer as a parameter sequence, and
135    /// returns the number of bytes written.
136    fn encode_to(&self, buffer: &mut [u8]) -> usize;
137}
138
139#[impl_for_tuples(1, 12)]
140#[tuple_types_custom_trait_bound(SolTypeEncode)]
141impl SolTypeParamsEncode for Tuple {
142    fn encode(&self) -> Vec<u8> {
143        let token = <Self as SolTypeEncode>::tokenize(self);
144        // NOTE: Parameter encoding excludes the top-level offset for a tuple with any
145        // dynamic type member(s).
146        let encoded_size =
147            if <<Self as SolTokenType>::TokenType<'_> as Encodable>::DYNAMIC {
148                token.tail_words()
149            } else {
150                token.head_words()
151            }
152            .checked_mul(32)
153            .unwrap();
154        let mut buffer = ink_prelude::vec![0u8; encoded_size];
155        let mut encoder = Encoder::new(buffer.as_mut_slice());
156        EncodableParams::encode_params(&token, &mut encoder);
157        buffer
158    }
159
160    fn encode_to(&self, buffer: &mut [u8]) -> usize {
161        let token = <Self as SolTypeEncode>::tokenize(self);
162        let mut encoder = Encoder::new(buffer);
163        EncodableParams::encode_params(&token, &mut encoder);
164        // NOTE: Parameter encoding excludes the top-level offset for a tuple with any
165        // dynamic type member(s).
166        let encoded_words =
167            if <<Self as SolTokenType>::TokenType<'_> as Encodable>::DYNAMIC {
168                token.tail_words()
169            } else {
170                token.head_words()
171            };
172        encoded_words.checked_mul(32).unwrap()
173    }
174}
175
176impl SolTypeParamsEncode for () {
177    fn encode(&self) -> Vec<u8> {
178        Vec::new()
179    }
180
181    fn encode_to(&self, _: &mut [u8]) -> usize {
182        0
183    }
184}
185
186mod private {
187    /// Seals implementations of `SolParamsEncode` and `SolParamsDecode`.
188    pub trait Sealed {}
189}