1use crate::{
2 item::{Constructor, Error, Event, Fallback, Function, Receive},
3 EventParam, InternalType, JsonAbi, Param, StateMutability,
4};
5use alloc::{
6 borrow::Cow,
7 collections::{BTreeMap, BTreeSet},
8 string::String,
9 vec::Vec,
10};
11use core::{
12 cmp::Ordering,
13 ops::{Deref, DerefMut},
14};
15
16#[derive(Clone, Debug)]
18#[allow(missing_copy_implementations)] pub struct ToSolConfig {
20 print_constructors: bool,
21 enums_as_udvt: bool,
22 for_sol_macro: bool,
23 one_contract: bool,
24}
25
26impl Default for ToSolConfig {
27 #[inline]
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl ToSolConfig {
34 #[inline]
36 pub const fn new() -> Self {
37 Self {
38 print_constructors: false,
39 enums_as_udvt: true,
40 for_sol_macro: false,
41 one_contract: false,
42 }
43 }
44
45 #[inline]
47 pub const fn print_constructors(mut self, yes: bool) -> Self {
48 self.print_constructors = yes;
49 self
50 }
51
52 #[inline]
55 pub const fn enums_as_udvt(mut self, yes: bool) -> Self {
56 self.enums_as_udvt = yes;
57 self
58 }
59
60 pub const fn for_sol_macro(mut self, yes: bool) -> Self {
64 self.for_sol_macro = yes;
65 self
66 }
67
68 pub const fn one_contract(mut self, yes: bool) -> Self {
73 self.one_contract = yes;
74 self
75 }
76}
77
78pub(crate) trait ToSol {
79 fn to_sol(&self, out: &mut SolPrinter<'_>);
80}
81
82pub(crate) struct SolPrinter<'a> {
83 s: &'a mut String,
85
86 name: &'a str,
88
89 print_param_location: bool,
92
93 config: ToSolConfig,
95}
96
97impl Deref for SolPrinter<'_> {
98 type Target = String;
99
100 #[inline]
101 fn deref(&self) -> &Self::Target {
102 self.s
103 }
104}
105
106impl DerefMut for SolPrinter<'_> {
107 #[inline]
108 fn deref_mut(&mut self) -> &mut Self::Target {
109 self.s
110 }
111}
112
113impl<'a> SolPrinter<'a> {
114 pub(crate) fn new(s: &'a mut String, name: &'a str, config: ToSolConfig) -> Self {
115 Self { s, name, print_param_location: false, config }
116 }
117
118 pub(crate) fn print(&mut self, abi: &'a JsonAbi) {
119 abi.to_sol_root(self);
120 }
121
122 fn indent(&mut self) {
123 self.push_str(" ");
124 }
125
126 fn push_ident(&mut self, s: &str) {
130 let s = self.normalize_ident(s);
131 self.push_str(&s);
132 }
133
134 fn normalize_ident<'b>(&self, s: &'b str) -> Cow<'b, str> {
140 if self.config.for_sol_macro && s.contains('$') {
141 Cow::Owned(s.replace('$', "_"))
142 } else {
143 Cow::Borrowed(s)
144 }
145 }
146}
147
148impl JsonAbi {
149 #[allow(unknown_lints, for_loops_over_fallibles)]
150 fn to_sol_root<'a>(&'a self, out: &mut SolPrinter<'a>) {
151 macro_rules! fmt {
152 ($iter:expr) => {
153 let mut any = false;
154 for x in $iter {
155 any = true;
156 out.indent();
157 x.to_sol(out);
158 out.push('\n');
159 }
160 if any {
161 out.push('\n');
162 }
163 };
164 }
165
166 let mut its = InternalTypes::new(out.name, out.config.enums_as_udvt);
167 its.visit_abi(self);
168
169 let one_contract = out.config.one_contract;
170
171 if !one_contract {
172 for (name, its) in &its.other {
173 if its.is_empty() {
174 continue;
175 }
176 out.push_str("library ");
177 out.push_str(name);
178 out.push_str(" {\n");
179 let prev = core::mem::replace(&mut out.name, name);
180 for it in its {
181 out.indent();
182 it.to_sol(out);
183 out.push('\n');
184 }
185 out.name = prev;
186 out.push_str("}\n\n");
187 }
188 }
189
190 out.push_str("interface ");
191 if !out.name.is_empty() {
192 out.s.push_str(out.name);
193 out.push(' ');
194 }
195 out.push('{');
196 out.push('\n');
197
198 if one_contract {
199 for (name, its) in &its.other {
200 if its.is_empty() {
201 continue;
202 }
203
204 out.indent();
205 out.push_str("// Types from `");
206 out.push_str(name);
207 out.push_str("`\n");
208 fmt!(its);
209 }
210 }
211 fmt!(its.this_its);
212 fmt!(self.errors());
213 fmt!(self.events());
214 if out.config.print_constructors {
215 fmt!(self.constructor());
216 }
217 fmt!(self.fallback);
218 fmt!(self.receive);
219 fmt!(self.functions());
220 out.pop(); out.push('}');
223 }
224}
225
226struct InternalTypes<'a> {
228 name: &'a str,
229 this_its: BTreeSet<It<'a>>,
230 other: BTreeMap<&'a String, BTreeSet<It<'a>>>,
231 enums_as_udvt: bool,
232}
233
234impl<'a> InternalTypes<'a> {
235 #[allow(clippy::missing_const_for_fn)]
236 fn new(name: &'a str, enums_as_udvt: bool) -> Self {
237 Self { name, this_its: BTreeSet::new(), other: BTreeMap::new(), enums_as_udvt }
238 }
239
240 fn visit_abi(&mut self, abi: &'a JsonAbi) {
241 if let Some(constructor) = &abi.constructor {
242 self.visit_params(&constructor.inputs);
243 }
244 for function in abi.functions() {
245 self.visit_params(&function.inputs);
246 self.visit_params(&function.outputs);
247 }
248 for error in abi.errors() {
249 self.visit_params(&error.inputs);
250 }
251 for event in abi.events() {
252 self.visit_event_params(&event.inputs);
253 }
254 }
255
256 fn visit_params(&mut self, params: &'a [Param]) {
257 for param in params {
258 self.visit_param(param);
259 }
260 }
261
262 fn visit_param(&mut self, param: &'a Param) {
263 self.extend(param.internal_type.as_ref(), ¶m.components, ¶m.ty);
264 self.visit_params(¶m.components);
265 }
266
267 fn visit_event_params(&mut self, params: &'a [EventParam]) {
268 for param in params {
269 self.visit_event_param(param);
270 }
271 }
272
273 fn visit_event_param(&mut self, param: &'a EventParam) {
274 self.extend(param.internal_type.as_ref(), ¶m.components, ¶m.ty);
275 self.visit_params(¶m.components);
276 }
277
278 fn extend(
279 &mut self,
280 internal_type: Option<&'a InternalType>,
281 components: &'a Vec<Param>,
282 real_ty: &'a str,
283 ) {
284 match internal_type {
285 None | Some(InternalType::AddressPayable(_) | InternalType::Contract(_)) => {}
286 Some(InternalType::Struct { contract, ty }) => {
287 self.extend_one(contract, It::new(ty, ItKind::Struct(components)));
288 }
289 Some(InternalType::Enum { contract, ty }) => {
290 if self.enums_as_udvt {
291 self.extend_one(contract, It::new(ty, ItKind::Enum));
292 }
293 }
294 Some(it @ InternalType::Other { contract, ty }) => {
295 if let Some(it) = it.other_specifier() {
297 if it.try_basic_solidity().is_err() {
298 let ty = ty.split('[').next().unwrap();
299 let real_ty = real_ty.split('[').next().unwrap();
300 self.extend_one(contract, It::new(ty, ItKind::Udvt(real_ty)));
301 }
302 }
303 }
304 }
305 }
306
307 fn extend_one(&mut self, contract: &'a Option<String>, it: It<'a>) {
308 let contract = contract.as_ref();
309 if let Some(contract) = contract {
310 if contract == self.name {
311 self.this_its.insert(it);
312 } else {
313 self.other.entry(contract).or_default().insert(it);
314 }
315 } else {
316 self.this_its.insert(it);
317 }
318 }
319}
320
321#[derive(PartialEq, Eq, PartialOrd, Ord)]
323struct It<'a> {
324 kind: ItKind<'a>,
326 name: &'a str,
327}
328
329#[derive(PartialEq, Eq)]
330enum ItKind<'a> {
331 Enum,
332 Udvt(&'a str),
333 Struct(&'a Vec<Param>),
334}
335
336impl PartialOrd for ItKind<'_> {
338 #[inline]
339 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
340 Some(self.cmp(other))
341 }
342}
343
344impl Ord for ItKind<'_> {
345 #[inline]
346 fn cmp(&self, other: &Self) -> Ordering {
347 match (self, other) {
348 (Self::Enum, Self::Enum) => Ordering::Equal,
349 (Self::Enum, _) => Ordering::Less,
350 (_, Self::Enum) => Ordering::Greater,
351
352 (Self::Udvt(_), Self::Udvt(_)) => Ordering::Equal,
353 (Self::Udvt(_), _) => Ordering::Less,
354 (_, Self::Udvt(_)) => Ordering::Greater,
355
356 (Self::Struct(_), Self::Struct(_)) => Ordering::Equal,
357 }
358 }
359}
360
361impl<'a> It<'a> {
362 #[inline]
363 fn new(ty_name: &'a str, kind: ItKind<'a>) -> Self {
364 Self {
365 kind,
366 name: ty_name.split('[').next().unwrap(),
368 }
369 }
370}
371
372impl ToSol for It<'_> {
373 fn to_sol(&self, out: &mut SolPrinter<'_>) {
374 match self.kind {
375 ItKind::Enum => {
376 out.push_str("type ");
377 out.push_ident(self.name);
378 out.push_str(" is uint8;");
379 }
380 ItKind::Udvt(ty) => {
381 out.push_str("type ");
382 out.push_ident(self.name);
383 out.push_str(" is ");
384 out.push_str(ty);
385 out.push(';');
386 }
387 ItKind::Struct(components) => {
388 out.push_str("struct ");
389 out.push_ident(self.name);
390 out.push_str(" {\n");
391 for component in components {
392 out.indent();
393 out.indent();
394 component.to_sol(out);
395 out.push_str(";\n");
396 }
397 out.indent();
398 out.push('}');
399 }
400 }
401 }
402}
403
404impl ToSol for Constructor {
405 fn to_sol(&self, out: &mut SolPrinter<'_>) {
406 AbiFunction::<'_, Param> {
407 kw: AbiFunctionKw::Constructor,
408 name: None,
409 inputs: &self.inputs,
410 visibility: None,
411 state_mutability: Some(self.state_mutability),
412 anonymous: false,
413 outputs: &[],
414 }
415 .to_sol(out);
416 }
417}
418
419impl ToSol for Event {
420 fn to_sol(&self, out: &mut SolPrinter<'_>) {
421 AbiFunction::<'_, EventParam> {
422 kw: AbiFunctionKw::Event,
423 name: Some(&self.name),
424 inputs: &self.inputs,
425 visibility: None,
426 state_mutability: None,
427 anonymous: self.anonymous,
428 outputs: &[],
429 }
430 .to_sol(out);
431 }
432}
433
434impl ToSol for Error {
435 fn to_sol(&self, out: &mut SolPrinter<'_>) {
436 AbiFunction::<'_, Param> {
437 kw: AbiFunctionKw::Error,
438 name: Some(&self.name),
439 inputs: &self.inputs,
440 visibility: None,
441 state_mutability: None,
442 anonymous: false,
443 outputs: &[],
444 }
445 .to_sol(out);
446 }
447}
448
449impl ToSol for Fallback {
450 fn to_sol(&self, out: &mut SolPrinter<'_>) {
451 AbiFunction::<'_, Param> {
452 kw: AbiFunctionKw::Fallback,
453 name: None,
454 inputs: &[],
455 visibility: Some(Visibility::External),
456 state_mutability: Some(self.state_mutability),
457 anonymous: false,
458 outputs: &[],
459 }
460 .to_sol(out);
461 }
462}
463
464impl ToSol for Receive {
465 fn to_sol(&self, out: &mut SolPrinter<'_>) {
466 AbiFunction::<'_, Param> {
467 kw: AbiFunctionKw::Receive,
468 name: None,
469 inputs: &[],
470 visibility: Some(Visibility::External),
471 state_mutability: Some(self.state_mutability),
472 anonymous: false,
473 outputs: &[],
474 }
475 .to_sol(out);
476 }
477}
478
479impl ToSol for Function {
480 fn to_sol(&self, out: &mut SolPrinter<'_>) {
481 AbiFunction::<'_, Param> {
482 kw: AbiFunctionKw::Function,
483 name: Some(&self.name),
484 inputs: &self.inputs,
485 visibility: Some(Visibility::External),
486 state_mutability: Some(self.state_mutability),
487 anonymous: false,
488 outputs: &self.outputs,
489 }
490 .to_sol(out);
491 }
492}
493
494struct AbiFunction<'a, IN> {
495 kw: AbiFunctionKw,
496 name: Option<&'a str>,
497 inputs: &'a [IN],
498 visibility: Option<Visibility>,
499 state_mutability: Option<StateMutability>,
500 anonymous: bool,
501 outputs: &'a [Param],
502}
503
504enum AbiFunctionKw {
505 Constructor,
506 Function,
507 Fallback,
508 Receive,
509 Error,
510 Event,
511}
512
513impl AbiFunctionKw {
514 #[inline]
515 const fn as_str(&self) -> &'static str {
516 match self {
517 Self::Constructor => "constructor",
518 Self::Function => "function",
519 Self::Fallback => "fallback",
520 Self::Receive => "receive",
521 Self::Error => "error",
522 Self::Event => "event",
523 }
524 }
525}
526
527enum Visibility {
528 External,
529}
530
531impl Visibility {
532 #[inline]
533 const fn as_str(&self) -> &'static str {
534 match self {
535 Self::External => "external",
536 }
537 }
538}
539
540impl<IN: ToSol> ToSol for AbiFunction<'_, IN> {
541 fn to_sol(&self, out: &mut SolPrinter<'_>) {
542 if matches!(
543 self.kw,
544 AbiFunctionKw::Function | AbiFunctionKw::Fallback | AbiFunctionKw::Receive
545 ) {
546 out.print_param_location = true;
547 }
548
549 out.push_str(self.kw.as_str());
557 if let Some(name) = self.name {
558 out.push(' ');
559 out.push_ident(name);
560 }
561
562 out.push('(');
563 for (i, input) in self.inputs.iter().enumerate() {
564 if i > 0 {
565 out.push_str(", ");
566 }
567 input.to_sol(out);
568 }
569 out.push(')');
570
571 if let Some(visibility) = &self.visibility {
572 out.push(' ');
573 out.push_str(visibility.as_str());
574 }
575
576 if let Some(state_mutability) = self.state_mutability {
577 if let Some(state_mutability) = state_mutability.as_str() {
578 out.push(' ');
579 out.push_str(state_mutability);
580 }
581 }
582
583 if !self.outputs.is_empty() {
584 out.push_str(" returns (");
585 for (i, output) in self.outputs.iter().enumerate() {
586 if i > 0 {
587 out.push_str(", ");
588 }
589 output.to_sol(out);
590 }
591 out.push(')');
592 }
593
594 if self.anonymous {
595 out.push_str(" anonymous");
596 }
597
598 out.push(';');
599
600 out.print_param_location = false;
601 }
602}
603
604impl ToSol for Param {
605 fn to_sol(&self, out: &mut SolPrinter<'_>) {
606 param(&self.ty, self.internal_type.as_ref(), false, &self.name, &self.components, out);
607 }
608}
609
610impl ToSol for EventParam {
611 fn to_sol(&self, out: &mut SolPrinter<'_>) {
612 param(
613 &self.ty,
614 self.internal_type.as_ref(),
615 self.indexed,
616 &self.name,
617 &self.components,
618 out,
619 );
620 }
621}
622
623fn param(
624 type_name: &str,
625 internal_type: Option<&InternalType>,
626 indexed: bool,
627 name: &str,
628 components: &[Param],
629 out: &mut SolPrinter<'_>,
630) {
631 let mut contract_name = None::<&str>;
632 let mut type_name = type_name;
633 let storage;
634 if let Some(it) = internal_type {
635 (contract_name, type_name) = match it {
636 InternalType::Contract(s) => {
637 let ty = if let Some(start) = s.find('[') {
638 storage = format!("address{}", &s[start..]);
639 &storage
640 } else {
641 "address"
642 };
643 (None, ty)
644 }
645 InternalType::Enum { .. } if !out.config.enums_as_udvt => (None, "uint8"),
646 InternalType::AddressPayable(ty) => (None, &ty[..]),
647 InternalType::Struct { contract, ty }
648 | InternalType::Enum { contract, ty }
649 | InternalType::Other { contract, ty } => {
650 if !out.config.one_contract {
651 (contract.as_deref(), &ty[..])
652 } else {
653 (None, &ty[..])
654 }
655 }
656 };
657 };
658
659 match type_name.strip_prefix("tuple") {
660 Some(rest) if rest.is_empty() || rest.starts_with('[') => {
664 out.push('(');
667 let prev = core::mem::replace(&mut out.print_param_location, false);
669 for (i, component) in components.iter().enumerate() {
670 if i > 0 {
671 out.push_str(", ");
672 }
673 param(
674 &component.ty,
675 component.internal_type.as_ref(), false,
677 "", &component.components,
679 out,
680 );
681 }
682 out.print_param_location = prev;
683 if components.len() == 1 {
685 out.push(',');
686 }
687 out.push(')');
688 out.push_str(rest);
690 }
691 _ => {
693 if let Some(contract_name) = contract_name {
694 if contract_name != out.name {
695 out.push_ident(contract_name);
696 out.push('.');
697 }
698 }
699 out.push_ident(type_name);
700 }
701 }
702
703 let is_memory = match type_name {
705 "bytes" | "string" => true,
707 s => s.ends_with(']') || !components.is_empty(),
708 };
709 if out.print_param_location && is_memory {
710 out.push_str(" memory");
711 }
712
713 if indexed {
714 out.push_str(" indexed");
715 }
716 if !name.is_empty() {
717 out.push(' ');
718 out.push_ident(name);
719 }
720}