1use crate::{EventParam, Param, StateMutability};
2use alloc::string::String;
3use alloy_primitives::Selector;
4use core::{fmt::Write, num::NonZeroUsize};
5use parser::{utils::ParsedSignature, ParameterSpecifier, TypeSpecifier, TypeStem};
6
7const PARAM_CAP: usize = 32;
9
10macro_rules! params_abi_tuple {
12 ($inputs:expr, $s:expr) => {
13 $s.push('(');
14 for (i, input) in $inputs.iter().enumerate() {
15 if i > 0 {
16 $s.push(',');
17 }
18 input.selector_type_raw($s);
19 }
20 $s.push(')');
21 };
22}
23
24pub(crate) fn signature(name: &str, inputs: &[Param], outputs: Option<&[Param]>) -> String {
26 let parens = 2 + outputs.is_some() as usize * 2;
27 let n_outputs = outputs.map(<[_]>::len).unwrap_or(0);
28 let cap = name.len() + parens + (inputs.len() + n_outputs) * PARAM_CAP;
29 let mut sig = String::with_capacity(cap);
30 sig.push_str(name);
31 params_abi_tuple(inputs, &mut sig);
32 if let Some(outputs) = outputs {
33 params_abi_tuple(outputs, &mut sig);
34 }
35 sig
36}
37
38pub(crate) fn full_signature(
39 name: &str,
40 inputs: &[Param],
41 outputs: Option<&[Param]>,
42 state_mutability: StateMutability,
43) -> String {
44 let parens = 2 + outputs.is_some() as usize * 2;
45 let n_outputs = outputs.map(<[_]>::len).unwrap_or(0);
46 let state_mutability_str = state_mutability.as_str();
47 let cap = "function ".len()
48 + name.len()
49 + parens
50 + (inputs.len() + n_outputs) * PARAM_CAP
51 + state_mutability_str.map(|s| s.len() + 1).unwrap_or(0);
52 let mut sig = String::with_capacity(cap);
53 sig.push_str("function ");
54 sig.push_str(name);
55 params_tuple(inputs, &mut sig);
56 if let Some(state_mutability_str) = state_mutability_str {
57 sig.push(' ');
58 sig.push_str(state_mutability_str);
59 }
60 if let Some(outputs) = outputs {
61 if !outputs.is_empty() {
62 sig.push_str(" returns ");
63 params_tuple(outputs, &mut sig);
64 }
65 }
66 sig
67}
68
69pub(crate) fn params_abi_tuple(params: &[Param], s: &mut String) {
71 params_abi_tuple!(params, s);
72}
73
74pub(crate) fn params_tuple(params: &[Param], s: &mut String) {
75 s.push('(');
76 for (i, input) in params.iter().enumerate() {
77 if i > 0 {
78 s.push_str(", ");
79 }
80 input.full_selector_type_raw(s);
81 if !input.name.is_empty() {
82 s.push(' ');
83 s.push_str(&input.name);
84 }
85 }
86 s.push(')');
87}
88
89pub(crate) fn event_signature(name: &str, inputs: &[EventParam]) -> String {
91 let mut preimage = String::with_capacity(name.len() + 2 + inputs.len() * PARAM_CAP);
92 preimage.push_str(name);
93 params_abi_tuple!(inputs, &mut preimage);
94 preimage
95}
96
97pub(crate) fn event_full_signature(name: &str, inputs: &[EventParam]) -> String {
99 let mut sig = String::with_capacity("event ".len() + name.len() + 2 + inputs.len() * PARAM_CAP);
100 sig.push_str("event ");
101 sig.push_str(name);
102 sig.push('(');
103 for (i, input) in inputs.iter().enumerate() {
104 if i > 0 {
105 sig.push_str(", ");
106 }
107 input.full_selector_type_raw(&mut sig);
108 if input.indexed {
109 sig.push_str(" indexed");
110 }
111 if !input.name.is_empty() {
112 sig.push(' ');
113 sig.push_str(&input.name);
114 }
115 }
116 sig.push(')');
117 sig
118}
119
120pub(crate) fn selector(preimage: &str) -> Selector {
122 alloy_primitives::keccak256(preimage.as_bytes())[..4].try_into().unwrap()
123}
124
125pub(crate) fn parse_maybe_prefixed<F: FnOnce(&str) -> R, R>(
127 mut s: &str,
128 prefix: &str,
129 parser: F,
130) -> R {
131 if let Some(stripped) = s.strip_prefix(prefix) {
132 if stripped.starts_with(char::is_whitespace) {
133 s = stripped.trim_start();
134 }
135 }
136 parser(s)
137}
138
139#[inline]
140pub(crate) fn parse_sig<const O: bool>(s: &str) -> parser::Result<ParsedSignature<Param>> {
141 parser::utils::parse_signature::<O, _, _>(s, |p| mk_param(p.name, p.ty))
142}
143
144#[inline]
145pub(crate) fn parse_event_sig(s: &str) -> parser::Result<ParsedSignature<EventParam>> {
146 parser::utils::parse_signature::<false, _, _>(s, mk_eparam)
147}
148
149pub(crate) fn mk_param(name: Option<&str>, ty: TypeSpecifier<'_>) -> Param {
150 let name = name.unwrap_or_default().into();
151 let internal_type = None;
152 match ty.stem {
153 TypeStem::Root(s) => {
154 Param { name, ty: ty_string(s.span(), &ty.sizes), components: vec![], internal_type }
155 }
156 TypeStem::Tuple(t) => Param {
157 name,
158 ty: ty_string("tuple", &ty.sizes),
159 components: t.types.into_iter().map(|ty| mk_param(None, ty)).collect(),
160 internal_type,
161 },
162 }
163}
164
165pub(crate) fn mk_eparam(spec: ParameterSpecifier<'_>) -> EventParam {
166 let p = mk_param(spec.name, spec.ty);
167 EventParam {
168 name: p.name,
169 ty: p.ty,
170 indexed: spec.indexed,
171 components: p.components,
172 internal_type: p.internal_type,
173 }
174}
175
176fn ty_string(s: &str, sizes: &[Option<NonZeroUsize>]) -> String {
177 let mut ty = String::with_capacity(s.len() + sizes.len() * 4);
178 ty.push_str(s);
179 for size in sizes {
180 ty.push('[');
181 if let Some(size) = size {
182 write!(ty, "{size}").unwrap();
183 }
184 ty.push(']');
185 }
186 ty
187}
188
189pub(crate) fn validate_identifier<E: serde::de::Error>(name: &str) -> Result<(), E> {
190 if !name.is_empty() && !parser::is_valid_identifier(name) {
191 return Err(serde::de::Error::invalid_value(
192 serde::de::Unexpected::Str(name),
193 &"a valid Solidity identifier",
194 ));
195 }
196 Ok(())
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 fn param(kind: &str) -> Param {
204 param2(kind, "param")
205 }
206
207 fn param2(kind: &str, name: &str) -> Param {
208 Param { ty: kind.into(), name: name.into(), internal_type: None, components: vec![] }
209 }
210
211 fn eparam(kind: &str) -> EventParam {
212 eparam_with_indexed(kind, "param", false)
213 }
214
215 fn eparam2(kind: &str, name: &str, indexed: bool) -> EventParam {
216 eparam_with_indexed(kind, name, indexed)
217 }
218
219 fn eparam_with_indexed(kind: &str, name: &str, indexed: bool) -> EventParam {
220 EventParam {
221 name: name.into(),
222 ty: kind.into(),
223 internal_type: None,
224 components: vec![],
225 indexed,
226 }
227 }
228
229 fn params(components: impl IntoIterator<Item = &'static str>) -> Param {
230 let components = components.into_iter().map(param).collect();
231 crate::Param { name: "param".into(), ty: "tuple".into(), internal_type: None, components }
232 }
233
234 fn full_signature_raw(
235 name: &str,
236 inputs: &[Param],
237 outputs: Option<&[Param]>,
238 state_mutability: StateMutability,
239 ) -> String {
240 full_signature(name, inputs, outputs, state_mutability)
241 }
242
243 fn full_signature_np(name: &str, inputs: &[Param], outputs: Option<&[Param]>) -> String {
244 full_signature_raw(name, inputs, outputs, StateMutability::NonPayable)
245 }
246
247 fn full_signature_with_sm(
248 name: &str,
249 inputs: &[Param],
250 outputs: Option<&[Param]>,
251 state_mutability: StateMutability,
252 ) -> String {
253 full_signature_raw(name, inputs, outputs, state_mutability)
254 }
255
256 #[test]
257 fn test_signature() {
258 assert_eq!(signature("foo", &[], None), "foo()");
259 assert_eq!(signature("bar", &[param("bool")], None), "bar(bool)");
260 assert_eq!(
261 signature("foo", &[param("bytes"), param("bytes32")], None),
262 "foo(bytes,bytes32)"
263 );
264 assert_eq!(
265 signature("foo", &[param("int"), params(["uint[]"]), param("string")], None),
266 "foo(int,(uint[]),string)"
267 );
268
269 assert_eq!(signature("foo", &[], Some(&[])), "foo()()");
270 assert_eq!(signature("foo", &[param("a")], Some(&[param("b")])), "foo(a)(b)");
271 assert_eq!(
272 signature("foo", &[param("a"), param("c")], Some(&[param("b"), param("d")])),
273 "foo(a,c)(b,d)"
274 );
275 }
276
277 #[test]
278 fn test_full_signature() {
279 assert_eq!(full_signature_np("foo", &[], None), "function foo()");
280 assert_eq!(full_signature_np("foo", &[], Some(&[])), "function foo()");
281 assert_eq!(full_signature_np("bar", &[param2("bool", "")], None), "function bar(bool)");
282 assert_eq!(
283 full_signature_np("bar", &[param2("bool", "")], Some(&[param2("bool", "")])),
284 "function bar(bool) returns (bool)"
285 );
286 assert_eq!(
287 full_signature_np(
288 "foo",
289 &[param2("address", "asset"), param2("uint256", "amount")],
290 None
291 ),
292 "function foo(address asset, uint256 amount)"
293 );
294 assert_eq!(
295 full_signature_np(
296 "foo",
297 &[param2("address", "asset")],
298 Some(&[param2("uint256", "amount")])
299 ),
300 "function foo(address asset) returns (uint256 amount)"
301 );
302
303 let components = vec![
304 param2("address", "pool"),
305 param2("uint256", "tokenInParam"),
306 param2("uint256", "tokenOutParam"),
307 param2("uint256", "maxPrice"),
308 ];
309 let swaps =
310 Param { name: "swaps".into(), ty: "tuple[]".into(), internal_type: None, components };
311
312 assert_eq!(
313 full_signature_with_sm(
314 "batchSwapExactIn",
315 &[
316 swaps,
317 param2("address", "tokenIn"),
318 param2("address", "tokenOut"),
319 param2("uint256", "totalAmountIn"),
320 param2("uint256", "minTotalAmountOut"),
321 ],
322 Some(&[param2("uint256", "totalAmountOut")]),
323 StateMutability::Payable,
324 ),
325 "function batchSwapExactIn(tuple(address pool, uint256 tokenInParam, uint256 tokenOutParam, uint256 maxPrice)[] swaps, address tokenIn, address tokenOut, uint256 totalAmountIn, uint256 minTotalAmountOut) payable returns (uint256 totalAmountOut)"
326 );
327
328 assert_eq!(
329 full_signature_with_sm(
330 "name",
331 &[],
332 Some(&[param2("string", "")]),
333 StateMutability::View
334 ),
335 "function name() view returns (string)"
336 );
337
338 assert_eq!(
339 full_signature_with_sm(
340 "calculateHash",
341 &[param2("address[]", "_addresses")],
342 Some(&[param2("bytes32", "")]),
343 StateMutability::Pure,
344 ),
345 "function calculateHash(address[] _addresses) pure returns (bytes32)"
346 );
347 }
348
349 #[test]
350 fn test_event_signature() {
351 assert_eq!(event_signature("foo", &[]), "foo()");
352 assert_eq!(event_signature("foo", &[eparam("bool")]), "foo(bool)");
353 assert_eq!(event_signature("foo", &[eparam("bool"), eparam("string")]), "foo(bool,string)");
354 }
355
356 #[test]
357 fn test_event_full_signature() {
358 assert_eq!(event_full_signature("foo", &[]), "event foo()");
359 assert_eq!(
360 event_full_signature("foo", &[eparam2("bool", "confirmed", true)]),
361 "event foo(bool indexed confirmed)"
362 );
363 assert_eq!(
364 event_full_signature(
365 "foo",
366 &[eparam2("bool", "confirmed", true), eparam2("string", "message", false)]
367 ),
368 "event foo(bool indexed confirmed, string message)"
369 );
370
371 let components = vec![
372 param2("uint256", "amount"),
373 param2("uint256", "startTime"),
374 param2("uint256", "interval"),
375 ];
376 let info = EventParam {
377 name: "info".into(),
378 ty: "tuple".into(),
379 internal_type: None,
380 components,
381 indexed: false,
382 };
383 assert_eq!(
384 event_full_signature(
385 "SetupDirectDebit",
386 &[
387 eparam2("address", "debtor", true),
388 eparam2("address", "receiver", true),
389 info,
390 ] ),
391 "event SetupDirectDebit(address indexed debtor, address indexed receiver, tuple(uint256 amount, uint256 startTime, uint256 interval) info)"
392 );
393 }
394
395 #[test]
396 fn test_parse_sig() {
397 let empty_sig = |name: &str, anonymous| ParsedSignature::<Param> {
398 name: name.into(),
399 inputs: vec![],
400 outputs: vec![],
401 anonymous,
402 state_mutability: None,
403 };
404 let sig = |name: &str, inputs, outputs| ParsedSignature::<Param> {
405 name: name.into(),
406 inputs,
407 outputs,
408 anonymous: false,
409 state_mutability: None,
410 };
411
412 assert_eq!(parse_sig::<true>("foo()"), Ok(empty_sig("foo", false)));
413 assert_eq!(parse_sig::<true>("foo()()"), Ok(empty_sig("foo", false)));
414 assert_eq!(parse_sig::<true>("foo()external()"), Ok(empty_sig("foo", false)));
415 assert_eq!(parse_sig::<true>("foo() \t ()"), Ok(empty_sig("foo", false)));
416 assert_eq!(parse_sig::<true>("foo() ()"), Ok(empty_sig("foo", false)));
417
418 assert_eq!(parse_sig::<false>("foo()"), Ok(empty_sig("foo", false)));
419 parse_sig::<false>("foo()()").unwrap_err();
420 parse_sig::<false>("foo()view external()").unwrap_err();
421 parse_sig::<false>("foo(,)()").unwrap_err();
422 parse_sig::<false>("foo(,)(,)").unwrap_err();
423
424 assert_eq!(parse_sig::<false>("foo()anonymous"), Ok(empty_sig("foo", true)));
425 assert_eq!(parse_sig::<false>("foo()\t anonymous"), Ok(empty_sig("foo", true)));
426
427 assert_eq!(parse_sig::<true>("foo()anonymous"), Ok(empty_sig("foo", true)));
428 assert_eq!(parse_sig::<true>("foo()\t anonymous"), Ok(empty_sig("foo", true)));
429
430 assert_eq!(parse_sig::<true>("foo() \t ()anonymous"), Ok(empty_sig("foo", true)));
431 assert_eq!(parse_sig::<true>("foo()()anonymous"), Ok(empty_sig("foo", true)));
432 assert_eq!(parse_sig::<true>("foo()()\t anonymous"), Ok(empty_sig("foo", true)));
433
434 assert_eq!(
435 parse_sig::<false>("foo(uint256 param)"),
436 Ok(sig("foo", vec![param("uint256")], vec![]))
437 );
438 assert_eq!(
439 parse_sig::<false>("bar(uint256 param)"),
440 Ok(sig("bar", vec![param("uint256")], vec![]))
441 );
442 assert_eq!(
443 parse_sig::<false>("baz(uint256 param, bool param)"),
444 Ok(sig("baz", vec![param("uint256"), param("bool")], vec![]))
445 );
446
447 assert_eq!(
448 parse_sig::<true>("f(a b)(c d)"),
449 Ok(sig("f", vec![param2("a", "b")], vec![param2("c", "d")]))
450 );
451
452 assert_eq!(
453 parse_sig::<true>("toString(uint number)(string s)"),
454 Ok(sig("toString", vec![param2("uint256", "number")], vec![param2("string", "s")]))
455 );
456
457 let mut sig_full = sig("toString", vec![param("uint256")], vec![param("string")]);
458 sig_full.state_mutability = Some(StateMutability::View);
459 assert_eq!(
460 parse_sig::<true>("toString(uint param) external view returns(string param)"),
461 Ok(sig_full)
462 );
463 }
464}