1use anyhow::{Result, bail};
2use convert_case::{Case, Casing};
3use handlebars::Handlebars;
4use sails_idl_parser::ast::{Program, TypeDecl, TypeDef, parse_idl};
5use serde::Serialize;
6use typedecl_to_sol::TypeDeclToSol;
7
8mod consts;
9mod typedecl_to_sol;
10
11#[derive(Serialize)]
12struct ArgData {
13 pub ty: String,
14 pub name: String,
15 pub mem_location: Option<String>,
16}
17
18#[derive(Serialize)]
19struct FunctionData {
20 pub name: String,
21 pub args: Vec<ArgData>,
22 pub reply_type: Option<String>,
23 pub reply_mem_location: Option<String>,
24 pub payable: bool,
25 pub returns_value: bool,
26}
27
28#[derive(Serialize)]
29struct EventArgData {
30 pub ty: String,
31 pub indexed: bool,
32 pub name: Option<String>,
33}
34
35#[derive(Serialize)]
36struct EventData {
37 pub name: String,
38 pub args: Vec<EventArgData>,
39}
40
41#[derive(Serialize)]
42struct ContractData {
43 pub pragma_version: String,
44 pub contract_name: String,
45 pub functions: Vec<FunctionData>,
46 pub events: Vec<EventData>,
47}
48
49pub struct GenerateContractResult {
50 pub data: Vec<u8>,
51 pub name: String,
52}
53
54pub fn generate_solidity_contract(idl_content: &str, name: &str) -> Result<GenerateContractResult> {
55 let program = parse_idl(idl_content)?;
56
57 let contract_name = name.to_string().to_case(Case::UpperCamel);
58
59 let contract_data = ContractData {
60 contract_name: contract_name.clone(),
61 pragma_version: consts::PRAGMA_VERSION.to_string(),
62 functions: functions_from_idl(&program)?,
63 events: events_from_idl(&program)?,
64 };
65
66 let mut handlebars = Handlebars::new();
67 handlebars.register_template_string("contract", consts::CONTRACT_TEMPLATE)?;
68
69 let mut contract = Vec::new();
70
71 handlebars.render_to_write("contract", &contract_data, &mut contract)?;
72
73 Ok(GenerateContractResult {
74 data: contract,
75 name: contract_name,
76 })
77}
78
79fn functions_from_idl(program: &Program) -> Result<Vec<FunctionData>> {
80 let mut functions = Vec::new();
81
82 if let Some(ctor) = program.ctor() {
83 for func in ctor.funcs() {
84 let mut args = Vec::new();
85 for p in func.params() {
86 let arg = ArgData {
87 ty: p.type_decl().get_ty()?,
88 name: p.name().to_case(Case::Camel),
89 mem_location: p.type_decl().get_mem_location(),
90 };
91 args.push(arg);
92 }
93 functions.push(FunctionData {
94 name: func.name().to_case(Case::Camel),
95 reply_type: None, reply_mem_location: None,
97 payable: has_tag(func.docs(), "#[payable]"),
98 returns_value: false, args,
100 });
101 }
102 }
103
104 for svc in program.services() {
105 for f in svc.funcs() {
106 let mut args = Vec::new();
107 for p in f.params() {
108 let arg = ArgData {
109 ty: p.type_decl().get_ty()?,
110 name: p.name().to_case(Case::Camel),
111 mem_location: p.type_decl().get_mem_location(),
112 };
113 args.push(arg);
114 }
115 functions.push(FunctionData {
116 name: format!("{}{}", svc.name(), f.name())
117 .as_str()
118 .to_case(Case::Camel),
119 reply_type: f.output().get_ty().ok(),
120 reply_mem_location: f.output().get_mem_location(),
121 payable: has_tag(f.docs(), "#[payable]"),
122 returns_value: has_tag(f.docs(), "#[returns_value]"),
123 args,
124 });
125 }
126 }
127
128 Ok(functions)
129}
130
131fn events_from_idl(program: &Program) -> Result<Vec<EventData>> {
132 let mut events = Vec::new();
133
134 for svc in program.services() {
135 for e in svc.events() {
136 let mut args = Vec::new();
137 match e.type_decl().unwrap() {
138 TypeDecl::Def(TypeDef::Struct(def)) => {
139 for f in def.fields() {
140 let arg = EventArgData {
141 ty: f.type_decl().get_ty()?,
142 indexed: has_tag(f.docs(), "#[indexed]"),
143 name: f.name().map(|name| name.to_case(Case::Camel)),
144 };
145 args.push(arg);
146 }
147 }
148 _ => bail!("Unsupported type"),
149 }
150 events.push(EventData {
151 name: e.name().to_string(),
152 args,
153 });
154 }
155 }
156
157 Ok(events)
158}
159
160fn has_tag(docs: &[String], tag: &str) -> bool {
161 docs.iter().any(|doc| doc.contains(tag))
162}