123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563 |
- use anyhow::{anyhow, Result};
- use crate::types::Idl;
- impl Idl {
- /// Create an [`Idl`] value with additional support for older specs based on the
- /// `idl.metadata.spec` field.
- ///
- /// If `spec` field is not specified, the conversion will fallback to the legacy IDL spec
- /// (pre Anchor v0.30.0).
- ///
- /// **Note:** For legacy IDLs, `idl.metadata.address` field is required to be populated with
- /// program's address otherwise an error will be returned.
- pub fn from_slice_with_conversion(idl: &[u8]) -> Result<Self> {
- let value = serde_json::from_slice::<serde_json::Value>(idl)?;
- let spec = value
- .get("metadata")
- .and_then(|m| m.get("spec"))
- .and_then(|spec| spec.as_str());
- match spec {
- // New standard
- Some(spec) => match spec {
- "0.1.0" => serde_json::from_value(value).map_err(Into::into),
- _ => Err(anyhow!("IDL spec not supported: `{spec}`")),
- },
- // Legacy
- None => serde_json::from_value::<legacy::Idl>(value).map(TryInto::try_into)?,
- }
- }
- }
- /// Legacy IDL spec (pre Anchor v0.30.0)
- mod legacy {
- use crate::types as t;
- use anyhow::{anyhow, Result};
- use heck::SnakeCase;
- use serde::{Deserialize, Serialize};
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- pub struct Idl {
- pub version: String,
- pub name: String,
- #[serde(skip_serializing_if = "Option::is_none", default)]
- pub docs: Option<Vec<String>>,
- #[serde(skip_serializing_if = "Vec::is_empty", default)]
- pub constants: Vec<IdlConst>,
- pub instructions: Vec<IdlInstruction>,
- #[serde(skip_serializing_if = "Vec::is_empty", default)]
- pub accounts: Vec<IdlTypeDefinition>,
- #[serde(skip_serializing_if = "Vec::is_empty", default)]
- pub types: Vec<IdlTypeDefinition>,
- #[serde(skip_serializing_if = "Option::is_none", default)]
- pub events: Option<Vec<IdlEvent>>,
- #[serde(skip_serializing_if = "Option::is_none", default)]
- pub errors: Option<Vec<IdlErrorCode>>,
- #[serde(skip_serializing_if = "Option::is_none", default)]
- pub metadata: Option<serde_json::Value>,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- pub struct IdlConst {
- pub name: String,
- #[serde(rename = "type")]
- pub ty: IdlType,
- pub value: String,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- pub struct IdlState {
- #[serde(rename = "struct")]
- pub strct: IdlTypeDefinition,
- pub methods: Vec<IdlInstruction>,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- pub struct IdlInstruction {
- pub name: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub docs: Option<Vec<String>>,
- pub accounts: Vec<IdlAccountItem>,
- pub args: Vec<IdlField>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub returns: Option<IdlType>,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- pub struct IdlAccounts {
- pub name: String,
- pub accounts: Vec<IdlAccountItem>,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(untagged)]
- pub enum IdlAccountItem {
- IdlAccount(IdlAccount),
- IdlAccounts(IdlAccounts),
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- pub struct IdlAccount {
- pub name: String,
- pub is_mut: bool,
- pub is_signer: bool,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub is_optional: Option<bool>,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub docs: Option<Vec<String>>,
- #[serde(skip_serializing_if = "Option::is_none", default)]
- pub pda: Option<IdlPda>,
- #[serde(skip_serializing_if = "Vec::is_empty", default)]
- pub relations: Vec<String>,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- pub struct IdlPda {
- pub seeds: Vec<IdlSeed>,
- #[serde(skip_serializing_if = "Option::is_none", default)]
- pub program_id: Option<IdlSeed>,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase", tag = "kind")]
- pub enum IdlSeed {
- Const(IdlSeedConst),
- Arg(IdlSeedArg),
- Account(IdlSeedAccount),
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- pub struct IdlSeedAccount {
- #[serde(rename = "type")]
- pub ty: IdlType,
- // account_ty points to the entry in the "accounts" section.
- // Some only if the `Account<T>` type is used.
- #[serde(skip_serializing_if = "Option::is_none")]
- pub account: Option<String>,
- pub path: String,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- pub struct IdlSeedArg {
- #[serde(rename = "type")]
- pub ty: IdlType,
- pub path: String,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- pub struct IdlSeedConst {
- #[serde(rename = "type")]
- pub ty: IdlType,
- pub value: serde_json::Value,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- pub struct IdlField {
- pub name: String,
- #[serde(skip_serializing_if = "Option::is_none")]
- pub docs: Option<Vec<String>>,
- #[serde(rename = "type")]
- pub ty: IdlType,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- pub struct IdlEvent {
- pub name: String,
- pub fields: Vec<IdlEventField>,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- pub struct IdlEventField {
- pub name: String,
- #[serde(rename = "type")]
- pub ty: IdlType,
- pub index: bool,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- pub struct IdlTypeDefinition {
- /// - `idl-parse`: always the name of the type
- /// - `idl-build`: full path if there is a name conflict, otherwise the name of the type
- pub name: String,
- /// Documentation comments
- #[serde(skip_serializing_if = "Option::is_none")]
- pub docs: Option<Vec<String>>,
- /// Generics, only supported with `idl-build`
- #[serde(skip_serializing_if = "Option::is_none")]
- pub generics: Option<Vec<String>>,
- /// Type definition, `struct` or `enum`
- #[serde(rename = "type")]
- pub ty: IdlTypeDefinitionTy,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "lowercase", tag = "kind")]
- pub enum IdlTypeDefinitionTy {
- Struct { fields: Vec<IdlField> },
- Enum { variants: Vec<IdlEnumVariant> },
- Alias { value: IdlType },
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- pub struct IdlEnumVariant {
- pub name: String,
- #[serde(skip_serializing_if = "Option::is_none", default)]
- pub fields: Option<EnumFields>,
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(untagged)]
- pub enum EnumFields {
- Named(Vec<IdlField>),
- Tuple(Vec<IdlType>),
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- pub enum IdlType {
- Bool,
- U8,
- I8,
- U16,
- I16,
- U32,
- I32,
- F32,
- U64,
- I64,
- F64,
- U128,
- I128,
- U256,
- I256,
- Bytes,
- String,
- PublicKey,
- Defined(String),
- Option(Box<IdlType>),
- Vec(Box<IdlType>),
- Array(Box<IdlType>, usize),
- GenericLenArray(Box<IdlType>, String),
- Generic(String),
- DefinedWithTypeArgs {
- name: String,
- args: Vec<IdlDefinedTypeArg>,
- },
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
- #[serde(rename_all = "camelCase")]
- pub enum IdlDefinedTypeArg {
- Generic(String),
- Value(String),
- Type(IdlType),
- }
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
- pub struct IdlErrorCode {
- pub code: u32,
- pub name: String,
- #[serde(skip_serializing_if = "Option::is_none", default)]
- pub msg: Option<String>,
- }
- impl TryFrom<Idl> for t::Idl {
- type Error = anyhow::Error;
- fn try_from(idl: Idl) -> Result<Self> {
- Ok(Self {
- address: idl
- .metadata
- .as_ref()
- .and_then(|m| m.get("address"))
- .and_then(|a| a.as_str())
- .ok_or_else(|| anyhow!("Program id missing in `idl.metadata.address` field"))?
- .into(),
- metadata: t::IdlMetadata {
- name: idl.name,
- version: idl.version,
- spec: t::IDL_SPEC.into(),
- description: Default::default(),
- repository: Default::default(),
- dependencies: Default::default(),
- contact: Default::default(),
- deployments: Default::default(),
- },
- docs: idl.docs.unwrap_or_default(),
- instructions: idl.instructions.into_iter().map(Into::into).collect(),
- accounts: idl.accounts.clone().into_iter().map(Into::into).collect(),
- events: idl
- .events
- .clone()
- .unwrap_or_default()
- .into_iter()
- .map(Into::into)
- .collect(),
- errors: idl
- .errors
- .unwrap_or_default()
- .into_iter()
- .map(Into::into)
- .collect(),
- types: idl
- .types
- .into_iter()
- .map(Into::into)
- .chain(idl.accounts.into_iter().map(Into::into))
- .chain(idl.events.unwrap_or_default().into_iter().map(Into::into))
- .collect(),
- constants: idl.constants.into_iter().map(Into::into).collect(),
- })
- }
- }
- fn get_disc(prefix: &str, name: &str) -> Vec<u8> {
- use sha2::{Digest, Sha256};
- let mut hasher = Sha256::new();
- hasher.update(prefix);
- hasher.update(b":");
- hasher.update(name);
- hasher.finalize()[..8].into()
- }
- impl From<IdlInstruction> for t::IdlInstruction {
- fn from(value: IdlInstruction) -> Self {
- let name = value.name.to_snake_case();
- Self {
- discriminator: get_disc("global", &name),
- name,
- docs: value.docs.unwrap_or_default(),
- accounts: value.accounts.into_iter().map(Into::into).collect(),
- args: value.args.into_iter().map(Into::into).collect(),
- returns: value.returns.map(|r| r.into()),
- }
- }
- }
- impl From<IdlTypeDefinition> for t::IdlAccount {
- fn from(value: IdlTypeDefinition) -> Self {
- Self {
- discriminator: get_disc("account", &value.name),
- name: value.name,
- }
- }
- }
- impl From<IdlEvent> for t::IdlEvent {
- fn from(value: IdlEvent) -> Self {
- Self {
- discriminator: get_disc("event", &value.name),
- name: value.name,
- }
- }
- }
- impl From<IdlErrorCode> for t::IdlErrorCode {
- fn from(value: IdlErrorCode) -> Self {
- Self {
- name: value.name,
- code: value.code,
- msg: value.msg,
- }
- }
- }
- impl From<IdlConst> for t::IdlConst {
- fn from(value: IdlConst) -> Self {
- Self {
- name: value.name,
- docs: Default::default(),
- ty: value.ty.into(),
- value: value.value,
- }
- }
- }
- impl From<IdlDefinedTypeArg> for t::IdlGenericArg {
- fn from(value: IdlDefinedTypeArg) -> Self {
- match value {
- IdlDefinedTypeArg::Type(ty) => Self::Type { ty: ty.into() },
- IdlDefinedTypeArg::Value(value) => Self::Const { value },
- IdlDefinedTypeArg::Generic(generic) => Self::Type {
- ty: t::IdlType::Generic(generic),
- },
- }
- }
- }
- impl From<IdlTypeDefinition> for t::IdlTypeDef {
- fn from(value: IdlTypeDefinition) -> Self {
- Self {
- name: value.name,
- docs: value.docs.unwrap_or_default(),
- serialization: Default::default(),
- repr: Default::default(),
- generics: Default::default(),
- ty: value.ty.into(),
- }
- }
- }
- impl From<IdlEvent> for t::IdlTypeDef {
- fn from(value: IdlEvent) -> Self {
- Self {
- name: value.name,
- docs: Default::default(),
- serialization: Default::default(),
- repr: Default::default(),
- generics: Default::default(),
- ty: t::IdlTypeDefTy::Struct {
- fields: Some(t::IdlDefinedFields::Named(
- value
- .fields
- .into_iter()
- .map(|f| t::IdlField {
- name: f.name.to_snake_case(),
- docs: Default::default(),
- ty: f.ty.into(),
- })
- .collect(),
- )),
- },
- }
- }
- }
- impl From<IdlTypeDefinitionTy> for t::IdlTypeDefTy {
- fn from(value: IdlTypeDefinitionTy) -> Self {
- match value {
- IdlTypeDefinitionTy::Struct { fields } => Self::Struct {
- fields: fields
- .is_empty()
- .then(|| None)
- .unwrap_or_else(|| Some(fields.into())),
- },
- IdlTypeDefinitionTy::Enum { variants } => Self::Enum {
- variants: variants
- .into_iter()
- .map(|variant| t::IdlEnumVariant {
- name: variant.name,
- fields: variant.fields.map(|fields| match fields {
- EnumFields::Named(fields) => fields.into(),
- EnumFields::Tuple(tys) => t::IdlDefinedFields::Tuple(
- tys.into_iter().map(Into::into).collect(),
- ),
- }),
- })
- .collect(),
- },
- IdlTypeDefinitionTy::Alias { value } => Self::Type {
- alias: value.into(),
- },
- }
- }
- }
- impl From<IdlField> for t::IdlField {
- fn from(value: IdlField) -> Self {
- Self {
- name: value.name.to_snake_case(),
- docs: value.docs.unwrap_or_default(),
- ty: value.ty.into(),
- }
- }
- }
- impl From<Vec<IdlField>> for t::IdlDefinedFields {
- fn from(value: Vec<IdlField>) -> Self {
- Self::Named(value.into_iter().map(Into::into).collect())
- }
- }
- impl From<IdlType> for t::IdlType {
- fn from(value: IdlType) -> Self {
- match value {
- IdlType::PublicKey => t::IdlType::Pubkey,
- IdlType::Defined(name) => t::IdlType::Defined {
- name,
- generics: Default::default(),
- },
- IdlType::DefinedWithTypeArgs { name, args } => t::IdlType::Defined {
- name,
- generics: args.into_iter().map(Into::into).collect(),
- },
- IdlType::Option(ty) => t::IdlType::Option(ty.into()),
- IdlType::Vec(ty) => t::IdlType::Vec(ty.into()),
- IdlType::Array(ty, len) => t::IdlType::Array(ty.into(), t::IdlArrayLen::Value(len)),
- IdlType::GenericLenArray(ty, generic) => {
- t::IdlType::Array(ty.into(), t::IdlArrayLen::Generic(generic))
- }
- _ => serde_json::to_value(value)
- .and_then(serde_json::from_value)
- .unwrap(),
- }
- }
- }
- impl From<Box<IdlType>> for Box<t::IdlType> {
- fn from(value: Box<IdlType>) -> Self {
- Box::new((*value).into())
- }
- }
- impl From<IdlAccountItem> for t::IdlInstructionAccountItem {
- fn from(value: IdlAccountItem) -> Self {
- match value {
- IdlAccountItem::IdlAccount(acc) => Self::Single(t::IdlInstructionAccount {
- name: acc.name.to_snake_case(),
- docs: acc.docs.unwrap_or_default(),
- writable: acc.is_mut,
- signer: acc.is_signer,
- optional: acc.is_optional.unwrap_or_default(),
- address: Default::default(),
- pda: acc
- .pda
- .map(|pda| -> Result<t::IdlPda> {
- Ok(t::IdlPda {
- seeds: pda
- .seeds
- .into_iter()
- .map(TryInto::try_into)
- .collect::<Result<_>>()?,
- program: pda.program_id.map(TryInto::try_into).transpose()?,
- })
- })
- .transpose()
- .unwrap_or_default(),
- relations: acc.relations,
- }),
- IdlAccountItem::IdlAccounts(accs) => Self::Composite(t::IdlInstructionAccounts {
- name: accs.name.to_snake_case(),
- accounts: accs.accounts.into_iter().map(Into::into).collect(),
- }),
- }
- }
- }
- impl TryFrom<IdlSeed> for t::IdlSeed {
- type Error = anyhow::Error;
- fn try_from(value: IdlSeed) -> Result<Self> {
- let seed = match value {
- IdlSeed::Account(seed) => Self::Account(t::IdlSeedAccount {
- account: seed.account,
- path: seed.path,
- }),
- IdlSeed::Arg(seed) => Self::Arg(t::IdlSeedArg { path: seed.path }),
- IdlSeed::Const(seed) => Self::Const(t::IdlSeedConst {
- value: match seed.ty {
- IdlType::String => seed.value.to_string().as_bytes().into(),
- _ => return Err(anyhow!("Const seed conversion not supported")),
- },
- }),
- };
- Ok(seed)
- }
- }
- }
|