ink_primitives/sol/
result.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::SolType as AlloySolType;
16use ink_prelude::vec::Vec;
17
18use crate::sol::{
19    Error,
20    SolDecode,
21    SolEncode,
22    SolErrorDecode,
23    SolErrorEncode,
24    SolTypeDecode,
25    SolTypeEncode,
26};
27
28/// Solidity ABI encode return data.
29pub trait SolResultEncode<'a> {
30    /// Equivalent Solidity ABI type representation.
31    type SolType: SolTypeEncode;
32
33    /// Name of equivalent Solidity ABI type.
34    const SOL_NAME: &'static str =
35        <<Self::SolType as SolTypeEncode>::AlloyType as AlloySolType>::SOL_NAME;
36
37    /// Solidity ABI encode this type as return data.
38    fn encode(&'a self) -> Vec<u8>;
39}
40
41impl<'a, T> SolResultEncode<'a> for T
42where
43    T: SolEncode<'a>,
44{
45    type SolType = <T as SolEncode<'a>>::SolType;
46
47    fn encode(&'a self) -> Vec<u8> {
48        T::encode(self)
49    }
50}
51
52impl<'a, T, E> SolResultEncode<'a> for Result<T, E>
53where
54    T: SolEncode<'a>,
55    E: SolErrorEncode,
56{
57    type SolType = <T as SolEncode<'a>>::SolType;
58
59    fn encode(&'a self) -> Vec<u8> {
60        match self {
61            Ok(val) => val.encode(),
62            Err(err) => err.encode(),
63        }
64    }
65}
66
67/// Solidity ABI decode return data.
68// Note: We define and implement this here (e.g. as opposed to an implementation
69// in `ink_env`) because we need 2 generic implementations for `T: SolEncode` and
70// `Result<T: SolEncode, E>` which Rust only allows if `Result<T, E>: !SolEncode`
71// (i.e. `Result<T, E>` doesn't implement `SolEncode`). The latter negative condition is
72// only allowed in this crate (because `SolEncode` is defined in this crate) as per Rust's
73// coherence/orphan rules.
74//
75// Ref: <https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules>
76pub trait SolResultDecode {
77    /// Equivalent Solidity ABI type representation.
78    type SolType: SolTypeDecode;
79
80    /// Name of equivalent Solidity ABI type.
81    const SOL_NAME: &'static str =
82        <<Self::SolType as SolTypeDecode>::AlloyType as AlloySolType>::SOL_NAME;
83
84    /// Solidity ABI decode return data into this type.
85    fn decode(data: &[u8], did_revert: bool) -> Result<Self, SolResultDecodeError>
86    where
87        Self: Sized;
88}
89
90/// Error representing reason for failing to decode Solidity ABI encoded return data.
91#[derive(Debug, Copy, Clone, PartialEq, Eq)]
92pub enum SolResultDecodeError {
93    /// Tried to decode revert/error data into a non-Result type.
94    NonResultFromRevert,
95    /// A general decoding error.
96    Decode,
97}
98
99impl From<Error> for SolResultDecodeError {
100    fn from(_: Error) -> Self {
101        SolResultDecodeError::Decode
102    }
103}
104
105impl<T> SolResultDecode for T
106where
107    T: SolDecode,
108{
109    type SolType = <T as SolDecode>::SolType;
110
111    fn decode(data: &[u8], did_revert: bool) -> Result<Self, SolResultDecodeError>
112    where
113        Self: Sized,
114    {
115        if did_revert {
116            Err(SolResultDecodeError::NonResultFromRevert)
117        } else {
118            Ok(T::decode(data)?)
119        }
120    }
121}
122
123impl<T, E> SolResultDecode for Result<T, E>
124where
125    T: SolDecode,
126    E: SolErrorDecode,
127{
128    type SolType = <T as SolDecode>::SolType;
129
130    fn decode(data: &[u8], did_revert: bool) -> Result<Self, SolResultDecodeError>
131    where
132        Self: Sized,
133    {
134        if did_revert {
135            Ok(E::decode(data).map(|err| Err(err))?)
136        } else {
137            Ok(T::decode(data).map(|val| Ok(val))?)
138        }
139    }
140}