mod.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // SPDX-License-Identifier: Apache-2.0
  2. use crate::cli::IdlCommand;
  3. use anchor_syn::idl::{Idl, IdlAccountItem, IdlInstruction, IdlType, IdlTypeDefinitionTy};
  4. use itertools::Itertools;
  5. use serde_json::Value as JsonValue;
  6. use solang::abi::anchor::discriminator;
  7. use solang_parser::lexer::is_keyword;
  8. use std::{ffi::OsStr, fs::File, io::Write, path::PathBuf, process::exit};
  9. /// This subcommand generates a Solidity interface file from Anchor IDL file.
  10. /// The IDL file is json and lists all the instructions, events, structs, enums,
  11. /// etc. We have to avoid the numerous Solidity keywords, and retain any documentation.
  12. pub fn idl(idl_args: &IdlCommand) {
  13. for file in &idl_args.input {
  14. idl_file(file, &idl_args.output);
  15. }
  16. }
  17. fn idl_file(file: &OsStr, output: &Option<PathBuf>) {
  18. let f = match File::open(file) {
  19. Ok(s) => s,
  20. Err(e) => {
  21. eprintln!("{}: error: {}", file.to_string_lossy(), e);
  22. exit(1);
  23. }
  24. };
  25. let idl: Idl = match serde_json::from_reader(f) {
  26. Ok(idl) => idl,
  27. Err(e) => {
  28. eprintln!("{}: error: {}", file.to_string_lossy(), e);
  29. exit(1);
  30. }
  31. };
  32. let filename = format!("{}.sol", idl.name);
  33. let path = if let Some(base) = output {
  34. base.join(filename)
  35. } else {
  36. PathBuf::from(filename)
  37. };
  38. println!(
  39. "{}: info: creating '{}'",
  40. file.to_string_lossy(),
  41. path.display()
  42. );
  43. let f = match File::create(&path) {
  44. Ok(f) => f,
  45. Err(e) => {
  46. eprintln!("{}: error: {}", path.display(), e);
  47. exit(1);
  48. }
  49. };
  50. if let Err(e) = write_solidity(&idl, f) {
  51. eprintln!("{}: error: {}", path.display(), e);
  52. exit(1);
  53. }
  54. }
  55. fn write_solidity(idl: &Idl, mut f: File) -> Result<(), std::io::Error> {
  56. let mut ty_names = idl
  57. .types
  58. .iter()
  59. .map(|ty| (ty.name.to_string(), ty.name.to_string()))
  60. .collect::<Vec<(String, String)>>();
  61. if let Some(events) = &idl.events {
  62. events
  63. .iter()
  64. .for_each(|event| ty_names.push((event.name.to_string(), event.name.to_string())));
  65. }
  66. rename_keywords(&mut ty_names);
  67. for ty_def in &idl.types {
  68. if let IdlTypeDefinitionTy::Enum { variants } = &ty_def.ty {
  69. if variants.iter().any(|variant| variant.fields.is_some()) {
  70. eprintln!(
  71. "enum {} has variants with fields, not supported in Solidity\n",
  72. ty_def.name
  73. );
  74. continue;
  75. }
  76. let mut name_map = variants
  77. .iter()
  78. .map(|variant| (variant.name.to_string(), variant.name.to_string()))
  79. .collect::<Vec<(String, String)>>();
  80. rename_keywords(&mut name_map);
  81. docs(&mut f, 0, &ty_def.docs)?;
  82. let name = &ty_names.iter().find(|e| *e.0 == ty_def.name).unwrap().1;
  83. writeln!(f, "enum {name} {{")?;
  84. let mut iter = variants.iter().enumerate();
  85. let mut next = iter.next();
  86. while let Some((no, _)) = next {
  87. next = iter.next();
  88. writeln!(
  89. f,
  90. "\t{}{}",
  91. name_map[no].1,
  92. if next.is_some() { "," } else { "" }
  93. )?;
  94. }
  95. writeln!(f, "}}")?;
  96. }
  97. }
  98. for ty_def in &idl.types {
  99. if let IdlTypeDefinitionTy::Struct { fields } = &ty_def.ty {
  100. let badtys: Vec<String> = fields
  101. .iter()
  102. .filter_map(|field| idltype_to_solidity(&field.ty, &ty_names).err())
  103. .collect();
  104. if badtys.is_empty() {
  105. let mut name_map = fields
  106. .iter()
  107. .map(|field| (field.name.to_string(), field.name.to_string()))
  108. .collect::<Vec<(String, String)>>();
  109. rename_keywords(&mut name_map);
  110. docs(&mut f, 0, &ty_def.docs)?;
  111. let name = &ty_names.iter().find(|e| *e.0 == ty_def.name).unwrap().1;
  112. writeln!(f, "struct {name} {{")?;
  113. for (no, field) in fields.iter().enumerate() {
  114. docs(&mut f, 1, &field.docs)?;
  115. writeln!(
  116. f,
  117. "\t{}\t{};",
  118. idltype_to_solidity(&field.ty, &ty_names).unwrap(),
  119. name_map[no].1
  120. )?;
  121. }
  122. writeln!(f, "}}")?;
  123. } else {
  124. eprintln!(
  125. "struct {} has fields of type {} which is not supported on Solidity",
  126. ty_def.name,
  127. badtys.join(", ")
  128. );
  129. }
  130. }
  131. }
  132. if let Some(events) = &idl.events {
  133. for event in events {
  134. let badtys: Vec<String> = event
  135. .fields
  136. .iter()
  137. .filter_map(|field| idltype_to_solidity(&field.ty, &ty_names).err())
  138. .collect();
  139. if badtys.is_empty() {
  140. let mut name_map = event
  141. .fields
  142. .iter()
  143. .map(|field| (field.name.to_string(), field.name.to_string()))
  144. .collect::<Vec<(String, String)>>();
  145. rename_keywords(&mut name_map);
  146. let name = &ty_names.iter().find(|e| *e.0 == event.name).unwrap().1;
  147. writeln!(f, "event {name} {{")?;
  148. let mut iter = event.fields.iter().enumerate();
  149. let mut next = iter.next();
  150. while let Some((no, e)) = next {
  151. next = iter.next();
  152. writeln!(
  153. f,
  154. "\t{}\t{}{}{}",
  155. idltype_to_solidity(&e.ty, &ty_names).unwrap(),
  156. if e.index { " indexed " } else { " " },
  157. name_map[no].1,
  158. if next.is_some() { "," } else { "" }
  159. )?;
  160. }
  161. writeln!(f, "}}")?;
  162. } else {
  163. eprintln!(
  164. "event {} has fields of type {} which is not supported on Solidity",
  165. event.name,
  166. badtys.join(", ")
  167. );
  168. }
  169. }
  170. }
  171. docs(&mut f, 0, &idl.docs)?;
  172. if let Some(program_id) = program_id(idl) {
  173. writeln!(f, "@program_id(\"{}\")", program_id)?;
  174. }
  175. writeln!(f, "interface {} {{", idl.name)?;
  176. let mut instruction_names = idl
  177. .instructions
  178. .iter()
  179. .map(|instr| (instr.name.to_string(), instr.name.to_string()))
  180. .collect::<Vec<(String, String)>>();
  181. rename_keywords(&mut instruction_names);
  182. for instr in &idl.instructions {
  183. instruction(&mut f, instr, &instruction_names, &ty_names)?;
  184. }
  185. writeln!(f, "}}")?;
  186. Ok(())
  187. }
  188. fn instruction(
  189. f: &mut File,
  190. instr: &IdlInstruction,
  191. instruction_names: &[(String, String)],
  192. ty_names: &[(String, String)],
  193. ) -> std::io::Result<()> {
  194. let mut badtys: Vec<String> = instr
  195. .args
  196. .iter()
  197. .filter_map(|field| idltype_to_solidity(&field.ty, ty_names).err())
  198. .collect();
  199. if let Some(ty) = &instr.returns {
  200. if let Err(s) = idltype_to_solidity(ty, ty_names) {
  201. badtys.push(s);
  202. }
  203. }
  204. if badtys.is_empty() {
  205. docs(f, 1, &instr.docs)?;
  206. let name = &instruction_names
  207. .iter()
  208. .find(|e| *e.0 == instr.name)
  209. .unwrap()
  210. .1;
  211. // The anchor discriminator is what Solidity calls a selector
  212. let selector = discriminator("global", &instr.name);
  213. write!(
  214. f,
  215. "\t@selector([{}])\n\tfunction {}(",
  216. selector.iter().map(|v| format!("{v:#04x}")).join(","),
  217. if instr.name == "new" {
  218. "initialize"
  219. } else {
  220. name
  221. }
  222. )?;
  223. let mut iter = instr.args.iter();
  224. let mut next = iter.next();
  225. while let Some(e) = next {
  226. next = iter.next();
  227. write!(
  228. f,
  229. "{} {}{}",
  230. idltype_to_solidity(&e.ty, ty_names).unwrap(),
  231. e.name,
  232. if next.is_some() { "," } else { "" }
  233. )?;
  234. }
  235. let is_view = instr.returns.is_some() && !mutable_account_exists(&instr.accounts);
  236. write!(f, ") {}external", if is_view { "view " } else { "" })?;
  237. if let Some(ty) = &instr.returns {
  238. writeln!(
  239. f,
  240. " returns ({});",
  241. idltype_to_solidity(ty, ty_names).unwrap()
  242. )?;
  243. } else {
  244. writeln!(f, ";")?;
  245. }
  246. } else {
  247. eprintln!(
  248. "instructions {} has arguments of type {} which is not supported on Solidity",
  249. instr.name,
  250. badtys.join(", ")
  251. );
  252. }
  253. Ok(())
  254. }
  255. fn mutable_account_exists(accounts: &[IdlAccountItem]) -> bool {
  256. accounts.iter().any(|item| match item {
  257. IdlAccountItem::IdlAccount(acc) => acc.is_mut,
  258. IdlAccountItem::IdlAccounts(accs) => mutable_account_exists(&accs.accounts),
  259. })
  260. }
  261. fn docs(f: &mut File, indent: usize, docs: &Option<Vec<String>>) -> std::io::Result<()> {
  262. if let Some(docs) = docs {
  263. for doc in docs {
  264. for _ in 0..indent {
  265. write!(f, "\t")?;
  266. }
  267. writeln!(f, "/// {doc}")?;
  268. }
  269. }
  270. Ok(())
  271. }
  272. fn idltype_to_solidity(ty: &IdlType, ty_names: &[(String, String)]) -> Result<String, String> {
  273. match ty {
  274. IdlType::Bool => Ok("bool".to_string()),
  275. IdlType::U8 => Ok("uint8".to_string()),
  276. IdlType::I8 => Ok("int8".to_string()),
  277. IdlType::U16 => Ok("uint16".to_string()),
  278. IdlType::I16 => Ok("int16".to_string()),
  279. IdlType::U32 => Ok("uint32".to_string()),
  280. IdlType::I32 => Ok("int32".to_string()),
  281. IdlType::U64 => Ok("uint64".to_string()),
  282. IdlType::I64 => Ok("int64".to_string()),
  283. IdlType::U128 => Ok("uint128".to_string()),
  284. IdlType::I128 => Ok("int128".to_string()),
  285. IdlType::U256 => Ok("uint256".to_string()),
  286. IdlType::I256 => Ok("int256".to_string()),
  287. IdlType::F32 => Err("f32".to_string()),
  288. IdlType::F64 => Err("f64".to_string()),
  289. IdlType::Bytes => Ok("bytes".to_string()),
  290. IdlType::String => Ok("string".to_string()),
  291. IdlType::PublicKey => Ok("address".to_string()),
  292. IdlType::Option(ty) => Err(format!(
  293. "Option({})",
  294. match idltype_to_solidity(ty, ty_names) {
  295. Ok(ty) => ty,
  296. Err(ty) => ty,
  297. }
  298. )),
  299. IdlType::Defined(ty) => {
  300. if let Some(e) = ty_names.iter().find(|rename| rename.0 == *ty) {
  301. Ok(e.1.clone())
  302. } else {
  303. Ok(ty.into())
  304. }
  305. }
  306. IdlType::Vec(ty) => match idltype_to_solidity(ty, ty_names) {
  307. Ok(ty) => Ok(format!("{ty}[]")),
  308. Err(ty) => Err(format!("{ty}[]")),
  309. },
  310. IdlType::Array(ty, size) => match idltype_to_solidity(ty, ty_names) {
  311. Ok(ty) => Ok(format!("{ty}[{size}]")),
  312. Err(ty) => Err(format!("{ty}[{size}]")),
  313. },
  314. }
  315. }
  316. fn program_id(idl: &Idl) -> Option<&String> {
  317. if let Some(JsonValue::Object(metadata)) = &idl.metadata {
  318. if let Some(JsonValue::String(address)) = metadata.get("address") {
  319. return Some(address);
  320. }
  321. }
  322. None
  323. }
  324. /// There are many keywords in Solidity which are not keywords in Rust, so they may
  325. /// occur as field name, function name, etc. Rename those fields by prepending
  326. /// underscores until unique
  327. fn rename_keywords(name_map: &mut Vec<(String, String)>) {
  328. for i in 0..name_map.len() {
  329. let name = &name_map[i].0;
  330. if is_keyword(name) {
  331. let mut name = name.clone();
  332. loop {
  333. name = format!("_{name}");
  334. if name_map.iter().all(|(_, n)| *n != name) {
  335. break;
  336. }
  337. }
  338. name_map[i].1 = name;
  339. }
  340. }
  341. }