convert.rs 19 KB

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