convert.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. use anyhow::{anyhow, Result};
  2. use crate::types::Idl;
  3. /// Create an [`Idl`] value with additional support for older specs based on the
  4. /// `idl.metadata.spec` field.
  5. ///
  6. /// If `spec` field is not specified, the conversion will fallback to the legacy IDL spec
  7. /// (pre Anchor v0.30.1).
  8. ///
  9. /// **Note:** For legacy IDLs, `idl.metadata.address` field is required to be populated with
  10. /// program's address otherwise an error will be returned.
  11. pub fn convert_idl(idl: &[u8]) -> Result<Idl> {
  12. let value = serde_json::from_slice::<serde_json::Value>(idl)?;
  13. let spec = value
  14. .get("metadata")
  15. .and_then(|m| m.get("spec"))
  16. .and_then(|spec| spec.as_str());
  17. match spec {
  18. // New standard
  19. Some(spec) => match spec {
  20. "0.1.0" => serde_json::from_value(value).map_err(Into::into),
  21. _ => Err(anyhow!("IDL spec not supported: `{spec}`")),
  22. },
  23. // Legacy
  24. None => serde_json::from_value::<legacy::Idl>(value).map(TryInto::try_into)?,
  25. }
  26. }
  27. /// Legacy IDL spec (pre Anchor v0.30.1)
  28. mod legacy {
  29. use crate::types as t;
  30. use anyhow::{anyhow, Result};
  31. use heck::SnakeCase;
  32. use serde::{Deserialize, Serialize};
  33. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  34. pub struct Idl {
  35. pub version: String,
  36. pub name: String,
  37. #[serde(skip_serializing_if = "Option::is_none", default)]
  38. pub docs: Option<Vec<String>>,
  39. #[serde(skip_serializing_if = "Vec::is_empty", default)]
  40. pub constants: Vec<IdlConst>,
  41. pub instructions: Vec<IdlInstruction>,
  42. #[serde(skip_serializing_if = "Vec::is_empty", default)]
  43. pub accounts: Vec<IdlTypeDefinition>,
  44. #[serde(skip_serializing_if = "Vec::is_empty", default)]
  45. pub types: Vec<IdlTypeDefinition>,
  46. #[serde(skip_serializing_if = "Option::is_none", default)]
  47. pub events: Option<Vec<IdlEvent>>,
  48. #[serde(skip_serializing_if = "Option::is_none", default)]
  49. pub errors: Option<Vec<IdlErrorCode>>,
  50. #[serde(skip_serializing_if = "Option::is_none", default)]
  51. pub metadata: Option<serde_json::Value>,
  52. }
  53. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  54. pub struct IdlConst {
  55. pub name: String,
  56. #[serde(rename = "type")]
  57. pub ty: IdlType,
  58. pub value: String,
  59. }
  60. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  61. pub struct IdlState {
  62. #[serde(rename = "struct")]
  63. pub strct: IdlTypeDefinition,
  64. pub methods: Vec<IdlInstruction>,
  65. }
  66. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  67. pub struct IdlInstruction {
  68. pub name: String,
  69. #[serde(skip_serializing_if = "Option::is_none")]
  70. pub docs: Option<Vec<String>>,
  71. pub accounts: Vec<IdlAccountItem>,
  72. pub args: Vec<IdlField>,
  73. #[serde(skip_serializing_if = "Option::is_none")]
  74. pub returns: Option<IdlType>,
  75. }
  76. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  77. #[serde(rename_all = "camelCase")]
  78. pub struct IdlAccounts {
  79. pub name: String,
  80. pub accounts: Vec<IdlAccountItem>,
  81. }
  82. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  83. #[serde(untagged)]
  84. pub enum IdlAccountItem {
  85. IdlAccount(IdlAccount),
  86. IdlAccounts(IdlAccounts),
  87. }
  88. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  89. #[serde(rename_all = "camelCase")]
  90. pub struct IdlAccount {
  91. pub name: String,
  92. pub is_mut: bool,
  93. pub is_signer: bool,
  94. #[serde(skip_serializing_if = "Option::is_none")]
  95. pub is_optional: Option<bool>,
  96. #[serde(skip_serializing_if = "Option::is_none")]
  97. pub docs: Option<Vec<String>>,
  98. #[serde(skip_serializing_if = "Option::is_none", default)]
  99. pub pda: Option<IdlPda>,
  100. #[serde(skip_serializing_if = "Vec::is_empty", default)]
  101. pub relations: Vec<String>,
  102. }
  103. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  104. #[serde(rename_all = "camelCase")]
  105. pub struct IdlPda {
  106. pub seeds: Vec<IdlSeed>,
  107. #[serde(skip_serializing_if = "Option::is_none", default)]
  108. pub program_id: Option<IdlSeed>,
  109. }
  110. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  111. #[serde(rename_all = "camelCase", tag = "kind")]
  112. pub enum IdlSeed {
  113. Const(IdlSeedConst),
  114. Arg(IdlSeedArg),
  115. Account(IdlSeedAccount),
  116. }
  117. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  118. #[serde(rename_all = "camelCase")]
  119. pub struct IdlSeedAccount {
  120. #[serde(rename = "type")]
  121. pub ty: IdlType,
  122. // account_ty points to the entry in the "accounts" section.
  123. // Some only if the `Account<T>` type is used.
  124. #[serde(skip_serializing_if = "Option::is_none")]
  125. pub account: Option<String>,
  126. pub path: String,
  127. }
  128. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  129. #[serde(rename_all = "camelCase")]
  130. pub struct IdlSeedArg {
  131. #[serde(rename = "type")]
  132. pub ty: IdlType,
  133. pub path: String,
  134. }
  135. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  136. #[serde(rename_all = "camelCase")]
  137. pub struct IdlSeedConst {
  138. #[serde(rename = "type")]
  139. pub ty: IdlType,
  140. pub value: serde_json::Value,
  141. }
  142. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  143. pub struct IdlField {
  144. pub name: String,
  145. #[serde(skip_serializing_if = "Option::is_none")]
  146. pub docs: Option<Vec<String>>,
  147. #[serde(rename = "type")]
  148. pub ty: IdlType,
  149. }
  150. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  151. pub struct IdlEvent {
  152. pub name: String,
  153. pub fields: Vec<IdlEventField>,
  154. }
  155. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  156. pub struct IdlEventField {
  157. pub name: String,
  158. #[serde(rename = "type")]
  159. pub ty: IdlType,
  160. pub index: bool,
  161. }
  162. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  163. pub struct IdlTypeDefinition {
  164. /// - `idl-parse`: always the name of the type
  165. /// - `idl-build`: full path if there is a name conflict, otherwise the name of the type
  166. pub name: String,
  167. /// Documentation comments
  168. #[serde(skip_serializing_if = "Option::is_none")]
  169. pub docs: Option<Vec<String>>,
  170. /// Generics, only supported with `idl-build`
  171. #[serde(skip_serializing_if = "Option::is_none")]
  172. pub generics: Option<Vec<String>>,
  173. /// Type definition, `struct` or `enum`
  174. #[serde(rename = "type")]
  175. pub ty: IdlTypeDefinitionTy,
  176. }
  177. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  178. #[serde(rename_all = "lowercase", tag = "kind")]
  179. pub enum IdlTypeDefinitionTy {
  180. Struct { fields: Vec<IdlField> },
  181. Enum { variants: Vec<IdlEnumVariant> },
  182. Alias { value: IdlType },
  183. }
  184. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  185. pub struct IdlEnumVariant {
  186. pub name: String,
  187. #[serde(skip_serializing_if = "Option::is_none", default)]
  188. pub fields: Option<EnumFields>,
  189. }
  190. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  191. #[serde(untagged)]
  192. pub enum EnumFields {
  193. Named(Vec<IdlField>),
  194. Tuple(Vec<IdlType>),
  195. }
  196. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  197. #[serde(rename_all = "camelCase")]
  198. pub enum IdlType {
  199. Bool,
  200. U8,
  201. I8,
  202. U16,
  203. I16,
  204. U32,
  205. I32,
  206. F32,
  207. U64,
  208. I64,
  209. F64,
  210. U128,
  211. I128,
  212. U256,
  213. I256,
  214. Bytes,
  215. String,
  216. PublicKey,
  217. Defined(String),
  218. Option(Box<IdlType>),
  219. Vec(Box<IdlType>),
  220. Array(Box<IdlType>, usize),
  221. GenericLenArray(Box<IdlType>, String),
  222. Generic(String),
  223. DefinedWithTypeArgs {
  224. name: String,
  225. args: Vec<IdlDefinedTypeArg>,
  226. },
  227. }
  228. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
  229. #[serde(rename_all = "camelCase")]
  230. pub enum IdlDefinedTypeArg {
  231. Generic(String),
  232. Value(String),
  233. Type(IdlType),
  234. }
  235. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
  236. pub struct IdlErrorCode {
  237. pub code: u32,
  238. pub name: String,
  239. #[serde(skip_serializing_if = "Option::is_none", default)]
  240. pub msg: Option<String>,
  241. }
  242. impl TryFrom<Idl> for t::Idl {
  243. type Error = anyhow::Error;
  244. fn try_from(idl: Idl) -> Result<Self> {
  245. Ok(Self {
  246. address: idl
  247. .metadata
  248. .as_ref()
  249. .and_then(|m| m.get("address"))
  250. .and_then(|a| a.as_str())
  251. .ok_or_else(|| anyhow!("Program id missing in `idl.metadata.address` field"))?
  252. .into(),
  253. metadata: t::IdlMetadata {
  254. name: idl.name,
  255. version: idl.version,
  256. spec: t::IDL_SPEC.into(),
  257. description: Default::default(),
  258. repository: Default::default(),
  259. dependencies: Default::default(),
  260. contact: Default::default(),
  261. deployments: Default::default(),
  262. },
  263. docs: idl.docs.unwrap_or_default(),
  264. instructions: idl.instructions.into_iter().map(Into::into).collect(),
  265. accounts: idl.accounts.clone().into_iter().map(Into::into).collect(),
  266. events: idl
  267. .events
  268. .clone()
  269. .unwrap_or_default()
  270. .into_iter()
  271. .map(Into::into)
  272. .collect(),
  273. errors: idl
  274. .errors
  275. .unwrap_or_default()
  276. .into_iter()
  277. .map(Into::into)
  278. .collect(),
  279. types: idl
  280. .types
  281. .into_iter()
  282. .map(Into::into)
  283. .chain(idl.accounts.into_iter().map(Into::into))
  284. .chain(idl.events.unwrap_or_default().into_iter().map(Into::into))
  285. .collect(),
  286. constants: idl.constants.into_iter().map(Into::into).collect(),
  287. })
  288. }
  289. }
  290. fn get_disc(prefix: &str, name: &str) -> Vec<u8> {
  291. use sha2::{Digest, Sha256};
  292. let mut hasher = Sha256::new();
  293. hasher.update(prefix);
  294. hasher.update(b":");
  295. hasher.update(name);
  296. hasher.finalize()[..8].into()
  297. }
  298. impl From<IdlInstruction> for t::IdlInstruction {
  299. fn from(value: IdlInstruction) -> Self {
  300. let name = value.name.to_snake_case();
  301. Self {
  302. discriminator: get_disc("global", &name),
  303. name,
  304. docs: value.docs.unwrap_or_default(),
  305. accounts: value.accounts.into_iter().map(Into::into).collect(),
  306. args: value.args.into_iter().map(Into::into).collect(),
  307. returns: value.returns.map(|r| r.into()),
  308. }
  309. }
  310. }
  311. impl From<IdlTypeDefinition> for t::IdlAccount {
  312. fn from(value: IdlTypeDefinition) -> Self {
  313. Self {
  314. discriminator: get_disc("account", &value.name),
  315. name: value.name,
  316. }
  317. }
  318. }
  319. impl From<IdlEvent> for t::IdlEvent {
  320. fn from(value: IdlEvent) -> Self {
  321. Self {
  322. discriminator: get_disc("event", &value.name),
  323. name: value.name,
  324. }
  325. }
  326. }
  327. impl From<IdlErrorCode> for t::IdlErrorCode {
  328. fn from(value: IdlErrorCode) -> Self {
  329. Self {
  330. name: value.name,
  331. code: value.code,
  332. msg: value.msg,
  333. }
  334. }
  335. }
  336. impl From<IdlConst> for t::IdlConst {
  337. fn from(value: IdlConst) -> Self {
  338. Self {
  339. name: value.name,
  340. docs: Default::default(),
  341. ty: value.ty.into(),
  342. value: value.value,
  343. }
  344. }
  345. }
  346. impl From<IdlDefinedTypeArg> for t::IdlGenericArg {
  347. fn from(value: IdlDefinedTypeArg) -> Self {
  348. match value {
  349. IdlDefinedTypeArg::Type(ty) => Self::Type { ty: ty.into() },
  350. IdlDefinedTypeArg::Value(value) => Self::Const { value },
  351. IdlDefinedTypeArg::Generic(generic) => Self::Type {
  352. ty: t::IdlType::Generic(generic),
  353. },
  354. }
  355. }
  356. }
  357. impl From<IdlTypeDefinition> for t::IdlTypeDef {
  358. fn from(value: IdlTypeDefinition) -> Self {
  359. Self {
  360. name: value.name,
  361. docs: value.docs.unwrap_or_default(),
  362. serialization: Default::default(),
  363. repr: Default::default(),
  364. generics: Default::default(),
  365. ty: value.ty.into(),
  366. }
  367. }
  368. }
  369. impl From<IdlEvent> for t::IdlTypeDef {
  370. fn from(value: IdlEvent) -> Self {
  371. Self {
  372. name: value.name,
  373. docs: Default::default(),
  374. serialization: Default::default(),
  375. repr: Default::default(),
  376. generics: Default::default(),
  377. ty: t::IdlTypeDefTy::Struct {
  378. fields: Some(t::IdlDefinedFields::Named(
  379. value
  380. .fields
  381. .into_iter()
  382. .map(|f| t::IdlField {
  383. name: f.name.to_snake_case(),
  384. docs: Default::default(),
  385. ty: f.ty.into(),
  386. })
  387. .collect(),
  388. )),
  389. },
  390. }
  391. }
  392. }
  393. impl From<IdlTypeDefinitionTy> for t::IdlTypeDefTy {
  394. fn from(value: IdlTypeDefinitionTy) -> Self {
  395. match value {
  396. IdlTypeDefinitionTy::Struct { fields } => Self::Struct {
  397. fields: fields.is_empty().then(|| None).unwrap_or_else(|| {
  398. Some(t::IdlDefinedFields::Named(
  399. fields.into_iter().map(Into::into).collect(),
  400. ))
  401. }),
  402. },
  403. IdlTypeDefinitionTy::Enum { variants } => Self::Enum {
  404. variants: variants
  405. .into_iter()
  406. .map(|variant| t::IdlEnumVariant {
  407. name: variant.name,
  408. fields: variant.fields.map(|fields| match fields {
  409. EnumFields::Named(fields) => t::IdlDefinedFields::Named(
  410. fields.into_iter().map(Into::into).collect(),
  411. ),
  412. EnumFields::Tuple(tys) => t::IdlDefinedFields::Tuple(
  413. tys.into_iter().map(Into::into).collect(),
  414. ),
  415. }),
  416. })
  417. .collect(),
  418. },
  419. IdlTypeDefinitionTy::Alias { value } => Self::Type {
  420. alias: value.into(),
  421. },
  422. }
  423. }
  424. }
  425. impl From<IdlField> for t::IdlField {
  426. fn from(value: IdlField) -> Self {
  427. Self {
  428. name: value.name.to_snake_case(),
  429. docs: value.docs.unwrap_or_default(),
  430. ty: value.ty.into(),
  431. }
  432. }
  433. }
  434. impl From<IdlType> for t::IdlType {
  435. fn from(value: IdlType) -> Self {
  436. match value {
  437. IdlType::PublicKey => t::IdlType::Pubkey,
  438. IdlType::Defined(name) => t::IdlType::Defined {
  439. name,
  440. generics: Default::default(),
  441. },
  442. IdlType::DefinedWithTypeArgs { name, args } => t::IdlType::Defined {
  443. name,
  444. generics: args.into_iter().map(Into::into).collect(),
  445. },
  446. IdlType::Option(ty) => t::IdlType::Option(ty.into()),
  447. IdlType::Vec(ty) => t::IdlType::Vec(ty.into()),
  448. IdlType::Array(ty, len) => t::IdlType::Array(ty.into(), t::IdlArrayLen::Value(len)),
  449. IdlType::GenericLenArray(ty, generic) => {
  450. t::IdlType::Array(ty.into(), t::IdlArrayLen::Generic(generic))
  451. }
  452. _ => serde_json::to_value(value)
  453. .and_then(serde_json::from_value)
  454. .unwrap(),
  455. }
  456. }
  457. }
  458. impl From<Box<IdlType>> for Box<t::IdlType> {
  459. fn from(value: Box<IdlType>) -> Self {
  460. Box::new((*value).into())
  461. }
  462. }
  463. impl From<IdlAccountItem> for t::IdlInstructionAccountItem {
  464. fn from(value: IdlAccountItem) -> Self {
  465. match value {
  466. IdlAccountItem::IdlAccount(acc) => Self::Single(t::IdlInstructionAccount {
  467. name: acc.name.to_snake_case(),
  468. docs: acc.docs.unwrap_or_default(),
  469. writable: acc.is_mut,
  470. signer: acc.is_signer,
  471. optional: acc.is_optional.unwrap_or_default(),
  472. address: Default::default(),
  473. pda: acc
  474. .pda
  475. .map(|pda| -> Result<t::IdlPda> {
  476. Ok(t::IdlPda {
  477. seeds: pda
  478. .seeds
  479. .into_iter()
  480. .map(TryInto::try_into)
  481. .collect::<Result<_>>()?,
  482. program: pda.program_id.map(TryInto::try_into).transpose()?,
  483. })
  484. })
  485. .transpose()
  486. .unwrap_or_default(),
  487. relations: acc.relations,
  488. }),
  489. IdlAccountItem::IdlAccounts(accs) => Self::Composite(t::IdlInstructionAccounts {
  490. name: accs.name.to_snake_case(),
  491. accounts: accs.accounts.into_iter().map(Into::into).collect(),
  492. }),
  493. }
  494. }
  495. }
  496. impl TryFrom<IdlSeed> for t::IdlSeed {
  497. type Error = anyhow::Error;
  498. fn try_from(value: IdlSeed) -> Result<Self> {
  499. let seed = match value {
  500. IdlSeed::Account(seed) => Self::Account(t::IdlSeedAccount {
  501. account: seed.account,
  502. path: seed.path,
  503. }),
  504. IdlSeed::Arg(seed) => Self::Arg(t::IdlSeedArg { path: seed.path }),
  505. IdlSeed::Const(seed) => Self::Const(t::IdlSeedConst {
  506. value: match seed.ty {
  507. IdlType::String => seed.value.to_string().as_bytes().into(),
  508. _ => return Err(anyhow!("Const seed conversion not supported")),
  509. },
  510. }),
  511. };
  512. Ok(seed)
  513. }
  514. }
  515. }