fe_abi/
function.rs

1use fe_common::utils::keccak;
2
3use serde::Serialize;
4
5use super::types::AbiType;
6
7/// The mutability of a public function.
8#[derive(Serialize, Debug, PartialEq, Eq, Clone)]
9#[serde(rename_all = "lowercase")]
10pub enum StateMutability {
11    Pure,
12    View,
13    Nonpayable,
14    Payable,
15}
16
17pub enum SelfParam {
18    None,
19    Imm,
20    Mut,
21}
22pub enum CtxParam {
23    None,
24    Imm,
25    Mut,
26}
27
28impl StateMutability {
29    pub fn from_self_and_ctx_params(self_: SelfParam, ctx: CtxParam) -> Self {
30        // Check ABI conformity
31        // See https://github.com/ethereum/fe/issues/558
32        //
33        //              no self   |   self   |  mut self  |
34        //           ......................................
35        // no ctx    :    pure    |   view    |  payable  |
36        // ctx       :    view    |   view    |  payable  |
37        // mut ctx   :   payable  |  payable  |  payable  |
38
39        match (self_, ctx) {
40            (SelfParam::None, CtxParam::None) => StateMutability::Pure,
41            (SelfParam::None, CtxParam::Imm) => StateMutability::View,
42            (SelfParam::None, CtxParam::Mut) => StateMutability::Payable,
43            (SelfParam::Imm, CtxParam::None) => StateMutability::View,
44            (SelfParam::Imm, CtxParam::Imm) => StateMutability::View,
45            (SelfParam::Imm, CtxParam::Mut) => StateMutability::Payable,
46            (SelfParam::Mut, _) => StateMutability::Payable,
47        }
48    }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
52pub struct AbiFunction {
53    #[serde(rename = "type")]
54    func_type: AbiFunctionType,
55    name: String,
56    inputs: Vec<AbiFunctionParamInner>,
57    outputs: Vec<AbiFunctionParamInner>,
58    #[serde(rename = "stateMutability")]
59    state_mutability: StateMutability,
60}
61
62impl AbiFunction {
63    pub fn new(
64        func_type: AbiFunctionType,
65        name: String,
66        args: Vec<(String, AbiType)>,
67        ret_ty: Option<AbiType>,
68        state_mutability: StateMutability,
69    ) -> Self {
70        let inputs = args
71            .into_iter()
72            .map(|(arg_name, arg_ty)| AbiFunctionParamInner::new(arg_name, arg_ty))
73            .collect();
74        let outputs = ret_ty.map_or_else(Vec::new, |ret_ty| {
75            vec![AbiFunctionParamInner::new("".into(), ret_ty)]
76        });
77
78        Self {
79            func_type,
80            name,
81            inputs,
82            outputs,
83            state_mutability,
84        }
85    }
86
87    pub fn selector(&self) -> AbiFunctionSelector {
88        AbiFunctionSelector::new(self)
89    }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
93#[serde(rename_all = "lowercase")]
94pub enum AbiFunctionType {
95    Function,
96    Constructor,
97    Receive,
98    Payable,
99    Fallback,
100}
101
102pub struct AbiFunctionSelector {
103    selector_sig: String,
104}
105
106impl AbiFunctionSelector {
107    fn new(func_sig: &AbiFunction) -> Self {
108        let selector_sig = format!(
109            "{}({})",
110            func_sig.name,
111            func_sig
112                .inputs
113                .iter()
114                .map(|param| param.ty.selector_type_name())
115                .collect::<Vec<_>>()
116                .join(",")
117        );
118
119        Self { selector_sig }
120    }
121
122    pub fn selector_signature(&self) -> &str {
123        &self.selector_sig
124    }
125
126    pub fn selector_raw(&self) -> [u8; 4] {
127        keccak::full_as_bytes(self.selector_sig.as_bytes())[..4]
128            .try_into()
129            .unwrap()
130    }
131
132    /// Returns first 4 bytes of signature hash in hex.
133    pub fn hex(&self) -> String {
134        keccak::partial(self.selector_sig.as_bytes(), 4)
135    }
136}
137
138#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
139struct AbiFunctionParamInner {
140    name: String,
141    #[serde(flatten)]
142    ty: AbiType,
143}
144
145impl AbiFunctionParamInner {
146    fn new(name: String, ty: AbiType) -> Self {
147        Self { name, ty }
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use crate::types::AbiTupleField;
154
155    use super::*;
156    use serde_test::{assert_ser_tokens, Token};
157
158    fn simple_tuple() -> AbiType {
159        let u16_ty = AbiType::UInt(16);
160        let bool_ty = AbiType::Bool;
161        let field1 = AbiTupleField::new("field1".into(), u16_ty);
162        let field2 = AbiTupleField::new("field2".into(), bool_ty);
163
164        AbiType::Tuple(vec![field1, field2])
165    }
166
167    fn test_func(state_mutability: StateMutability) -> AbiFunction {
168        let i32_ty = AbiType::Int(32);
169        let tuple_ty = simple_tuple();
170        let u64_ty = AbiType::UInt(64);
171
172        AbiFunction::new(
173            AbiFunctionType::Function,
174            "test_func".into(),
175            vec![("arg1".into(), i32_ty), ("arg2".into(), tuple_ty)],
176            Some(u64_ty),
177            state_mutability,
178        )
179    }
180
181    #[test]
182    fn serialize_func() {
183        let func = test_func(StateMutability::Payable);
184
185        assert_ser_tokens(
186            &func,
187            &[
188                Token::Struct {
189                    name: "AbiFunction",
190                    len: 5,
191                },
192                Token::Str("type"),
193                Token::UnitVariant {
194                    name: "AbiFunctionType",
195                    variant: "function",
196                },
197                Token::String("name"),
198                Token::String("test_func"),
199                Token::Str("inputs"),
200                Token::Seq { len: Some(2) },
201                Token::Map { len: None },
202                Token::String("name"),
203                Token::String("arg1"),
204                Token::String("type"),
205                Token::String("int32"),
206                Token::MapEnd,
207                Token::Map { len: None },
208                Token::String("name"),
209                Token::String("arg2"),
210                Token::String("type"),
211                Token::String("tuple"),
212                Token::String("components"),
213                Token::Seq { len: Some(2) },
214                Token::Map { len: None },
215                Token::String("name"),
216                Token::String("field1"),
217                Token::String("type"),
218                Token::String("uint16"),
219                Token::MapEnd,
220                Token::Map { len: None },
221                Token::String("name"),
222                Token::String("field2"),
223                Token::String("type"),
224                Token::String("bool"),
225                Token::MapEnd,
226                Token::SeqEnd,
227                Token::MapEnd,
228                Token::SeqEnd,
229                Token::Str("outputs"),
230                Token::Seq { len: Some(1) },
231                Token::Map { len: None },
232                Token::String("name"),
233                Token::String(""),
234                Token::String("type"),
235                Token::String("uint64"),
236                Token::MapEnd,
237                Token::SeqEnd,
238                Token::Str("stateMutability"),
239                Token::UnitVariant {
240                    name: "StateMutability",
241                    variant: "payable",
242                },
243                Token::StructEnd,
244            ],
245        )
246    }
247
248    #[test]
249    fn test_state_mutability() {
250        assert_eq!(
251            StateMutability::from_self_and_ctx_params(SelfParam::None, CtxParam::None),
252            StateMutability::Pure
253        );
254        assert_eq!(
255            StateMutability::from_self_and_ctx_params(SelfParam::None, CtxParam::Imm),
256            StateMutability::View
257        );
258        assert_eq!(
259            StateMutability::from_self_and_ctx_params(SelfParam::None, CtxParam::Mut),
260            StateMutability::Payable
261        );
262
263        assert_eq!(
264            StateMutability::from_self_and_ctx_params(SelfParam::Imm, CtxParam::None),
265            StateMutability::View
266        );
267        assert_eq!(
268            StateMutability::from_self_and_ctx_params(SelfParam::Imm, CtxParam::Imm),
269            StateMutability::View
270        );
271        assert_eq!(
272            StateMutability::from_self_and_ctx_params(SelfParam::Imm, CtxParam::Mut),
273            StateMutability::Payable
274        );
275
276        assert_eq!(
277            StateMutability::from_self_and_ctx_params(SelfParam::Mut, CtxParam::None),
278            StateMutability::Payable
279        );
280        assert_eq!(
281            StateMutability::from_self_and_ctx_params(SelfParam::Mut, CtxParam::Imm),
282            StateMutability::Payable
283        );
284        assert_eq!(
285            StateMutability::from_self_and_ctx_params(SelfParam::Mut, CtxParam::Mut),
286            StateMutability::Payable
287        );
288
289        let pure_func = test_func(StateMutability::Pure);
290        assert_eq!(pure_func.state_mutability, StateMutability::Pure);
291
292        let impure_func = test_func(StateMutability::Payable);
293        assert_eq!(impure_func.state_mutability, StateMutability::Payable);
294    }
295
296    #[test]
297    fn func_selector() {
298        let func = test_func(StateMutability::Payable);
299        let selector = func.selector();
300
301        debug_assert_eq!(
302            selector.selector_signature(),
303            "test_func(int32,(uint16,bool))"
304        );
305        debug_assert_eq!(selector.hex(), "79c3c8b2");
306    }
307}