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    fn encode(&'a self) -> Vec<u8> {
87        let params = self.to_sol_type();
88        let token = <<Self as SolEncode>::SolType as SolTypeEncode>::tokenize(&params);
89        // NOTE: Parameter encoding excludes the top-level offset for a tuple with any
90        // dynamic type member(s).
91        let encoded_size = if <<<Self as SolEncode>::SolType as SolTokenType>::TokenType<
92            'a,
93        > as Encodable>::DYNAMIC
94        {
95            token.tail_words()
96        } else {
97            token.head_words()
98        }
99        .checked_mul(32)
100        .unwrap();
101        let mut buffer = ink_prelude::vec![0u8; encoded_size];
102        let mut encoder = Encoder::new(buffer.as_mut_slice());
103        EncodableParams::encode_params(&token, &mut encoder);
104        buffer
105    }
106
107    fn encode_to(&'a self, buffer: &mut [u8]) -> usize {
108        let params = self.to_sol_type();
109        let token = <<Self as SolEncode>::SolType as SolTypeEncode>::tokenize(&params);
110        let mut encoder = Encoder::new(buffer);
111        EncodableParams::encode_params(&token, &mut encoder);
112        // NOTE: Parameter encoding excludes the top-level offset for a tuple with any
113        // dynamic type member(s).
114        let encoded_words = if <<<Self as SolEncode>::SolType as SolTokenType>::TokenType<
115            'a,
116        > as Encodable>::DYNAMIC
117        {
118            token.tail_words()
119        } else {
120            token.head_words()
121        };
122        encoded_words.checked_mul(32).unwrap()
123    }
124}
125
126// Optimized implementations for unit (i.e. `()`).
127impl SolParamsDecode for () {
128    fn decode(_: &[u8]) -> Result<Self, Error> {
129        // NOTE: Solidity ABI decoding doesn't validate input length.
130        Ok(())
131    }
132}
133
134impl SolParamsEncode<'_> for () {
135    fn encode(&self) -> Vec<u8> {
136        Vec::new()
137    }
138
139    fn encode_to(&self, _: &mut [u8]) -> usize {
140        0
141    }
142}
143
144#[impl_for_tuples(12)]
145#[tuple_types_no_default_trait_bound]
146impl private::Sealed for Tuple {}
147
148mod private {
149    /// Seals implementations of `SolParamsEncode` and `SolParamsDecode`.
150    pub trait Sealed {}
151}