Skip to main content

trident_idl_spec/
spec.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5pub struct Idl {
6    // New format (v30+)
7    #[serde(default, skip_serializing_if = "is_default")]
8    pub address: String,
9    #[serde(default, skip_serializing_if = "is_default")]
10    pub metadata: IdlMetadata,
11    // Legacy format (v29) - name and version at root level
12    #[serde(default, skip_serializing_if = "is_default")]
13    pub name: Option<String>,
14    #[serde(default, skip_serializing_if = "is_default")]
15    pub version: Option<String>,
16    // Common fields
17    #[serde(default, skip_serializing_if = "is_default")]
18    pub docs: Vec<String>,
19    pub instructions: Vec<IdlInstruction>,
20    #[serde(default, skip_serializing_if = "is_default")]
21    pub accounts: Vec<IdlAccount>,
22    #[serde(default, skip_serializing_if = "is_default")]
23    pub events: Vec<IdlEvent>,
24    #[serde(default, skip_serializing_if = "is_default")]
25    pub errors: Vec<IdlErrorCode>,
26    #[serde(default, skip_serializing_if = "is_default")]
27    pub types: Vec<IdlTypeDef>,
28    #[serde(default, skip_serializing_if = "is_default")]
29    pub constants: Vec<IdlConst>,
30}
31
32impl Idl {
33    /// Get the program name, handling both v29 (root level) and v30+ (metadata) formats
34    pub fn get_name(&self) -> &str {
35        self.name.as_deref().unwrap_or(&self.metadata.name)
36    }
37
38    /// Get the program version, handling both v29 (root level) and v30+ (metadata) formats
39    pub fn get_version(&self) -> &str {
40        self.version.as_deref().unwrap_or(&self.metadata.version)
41    }
42}
43
44#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
45pub struct IdlMetadata {
46    #[serde(default)]
47    pub name: String,
48    #[serde(default)]
49    pub version: String,
50    #[serde(default, skip_serializing_if = "is_default")]
51    pub spec: String,
52    #[serde(skip_serializing_if = "is_default")]
53    pub description: Option<String>,
54    #[serde(skip_serializing_if = "is_default")]
55    pub repository: Option<String>,
56    #[serde(default, skip_serializing_if = "is_default")]
57    pub dependencies: Vec<IdlDependency>,
58    #[serde(skip_serializing_if = "is_default")]
59    pub contact: Option<String>,
60    #[serde(skip_serializing_if = "is_default")]
61    pub deployments: Option<IdlDeployments>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
65pub struct IdlDependency {
66    pub name: String,
67    pub version: String,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
71pub struct IdlDeployments {
72    pub mainnet: Option<String>,
73    pub testnet: Option<String>,
74    pub devnet: Option<String>,
75    pub localnet: Option<String>,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
79pub struct IdlInstruction {
80    pub name: String,
81    #[serde(default, skip_serializing_if = "is_default")]
82    pub docs: Vec<String>,
83    #[serde(default, skip_serializing_if = "is_default")]
84    pub discriminator: IdlDiscriminator,
85    pub accounts: Vec<IdlInstructionAccountItem>,
86    pub args: Vec<IdlField>,
87    #[serde(skip_serializing_if = "is_default")]
88    pub returns: Option<IdlType>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
92#[serde(untagged)]
93pub enum IdlInstructionAccountItem {
94    Composite(IdlInstructionAccounts),
95    Single(IdlInstructionAccount),
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
99pub struct IdlInstructionAccount {
100    pub name: String,
101    #[serde(default, skip_serializing_if = "is_default")]
102    pub docs: Vec<String>,
103    #[serde(default, skip_serializing_if = "is_default", alias = "isMut")]
104    pub writable: bool,
105    #[serde(default, skip_serializing_if = "is_default", alias = "isSigner")]
106    pub signer: bool,
107    #[serde(default, skip_serializing_if = "is_default")]
108    pub optional: bool,
109    #[serde(skip_serializing_if = "is_default")]
110    pub address: Option<String>,
111    #[serde(skip_serializing_if = "is_default")]
112    pub pda: Option<IdlPda>,
113    #[serde(default, skip_serializing_if = "is_default")]
114    pub relations: Vec<String>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
118pub struct IdlInstructionAccounts {
119    pub name: String,
120    pub accounts: Vec<IdlInstructionAccountItem>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
124pub struct IdlPda {
125    pub seeds: Vec<IdlSeed>,
126    #[serde(skip_serializing_if = "is_default")]
127    pub program: Option<IdlSeed>,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
131#[serde(tag = "kind", rename_all = "lowercase")]
132pub enum IdlSeed {
133    Const(IdlSeedConst),
134    Arg(IdlSeedArg),
135    Account(IdlSeedAccount),
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
139pub struct IdlSeedConst {
140    pub value: Vec<u8>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
144pub struct IdlSeedArg {
145    pub path: String,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
149pub struct IdlSeedAccount {
150    pub path: String,
151    #[serde(skip_serializing_if = "is_default")]
152    pub account: Option<String>,
153}
154
155/// Account definition - supports both v29 (with embedded type) and v30+ (name + discriminator only)
156#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
157pub struct IdlAccount {
158    pub name: String,
159    #[serde(default, skip_serializing_if = "is_default")]
160    pub discriminator: IdlDiscriminator,
161    /// v29 format has embedded type definition
162    #[serde(default, skip_serializing_if = "is_default", rename = "type")]
163    pub ty: Option<IdlTypeDefTy>,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
167pub struct IdlEvent {
168    pub name: String,
169    #[serde(default, skip_serializing_if = "is_default")]
170    pub discriminator: IdlDiscriminator,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
174pub struct IdlConst {
175    pub name: String,
176    #[serde(default, skip_serializing_if = "is_default")]
177    pub docs: Vec<String>,
178    #[serde(rename = "type")]
179    pub ty: IdlType,
180    pub value: String,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
184pub struct IdlErrorCode {
185    pub code: u32,
186    pub name: String,
187    #[serde(skip_serializing_if = "is_default")]
188    pub msg: Option<String>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
192pub struct IdlField {
193    pub name: String,
194    #[serde(default, skip_serializing_if = "is_default")]
195    pub docs: Vec<String>,
196    #[serde(rename = "type")]
197    pub ty: IdlType,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
201pub struct IdlTypeDef {
202    pub name: String,
203    #[serde(default, skip_serializing_if = "is_default")]
204    pub docs: Vec<String>,
205    #[serde(default, skip_serializing_if = "is_default")]
206    pub serialization: IdlSerialization,
207    #[serde(skip_serializing_if = "is_default")]
208    pub repr: Option<IdlRepr>,
209    #[serde(default, skip_serializing_if = "is_default")]
210    pub generics: Vec<IdlTypeDefGeneric>,
211    #[serde(rename = "type")]
212    pub ty: IdlTypeDefTy,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
216#[serde(rename_all = "lowercase")]
217#[non_exhaustive]
218pub enum IdlSerialization {
219    #[default]
220    Borsh,
221    Bytemuck,
222    BytemuckUnsafe,
223    Custom(String),
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
227#[serde(tag = "kind", rename_all = "lowercase")]
228#[non_exhaustive]
229pub enum IdlRepr {
230    Rust(IdlReprModifier),
231    C(IdlReprModifier),
232    Transparent,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
236pub struct IdlReprModifier {
237    #[serde(default, skip_serializing_if = "is_default")]
238    pub packed: bool,
239    #[serde(skip_serializing_if = "is_default")]
240    pub align: Option<usize>,
241}
242
243/// Generic type parameter definition - supports both v29 (string) and v30+ (object) formats
244#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
245#[serde(untagged)]
246pub enum IdlTypeDefGeneric {
247    /// v29 format: just a string like "T"
248    Simple(String),
249    /// v30+ format: object with kind and name
250    Complex(IdlTypeDefGenericComplex),
251}
252
253impl IdlTypeDefGeneric {
254    /// Get the name of the generic parameter
255    pub fn get_name(&self) -> &str {
256        match self {
257            IdlTypeDefGeneric::Simple(name) => name,
258            IdlTypeDefGeneric::Complex(complex) => match complex {
259                IdlTypeDefGenericComplex::Type { name } => name,
260                IdlTypeDefGenericComplex::Const { name, .. } => name,
261            },
262        }
263    }
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
267#[serde(tag = "kind", rename_all = "lowercase")]
268pub enum IdlTypeDefGenericComplex {
269    Type {
270        name: String,
271    },
272    Const {
273        name: String,
274        #[serde(rename = "type")]
275        ty: String,
276    },
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
280#[serde(tag = "kind", rename_all = "lowercase")]
281pub enum IdlTypeDefTy {
282    Struct {
283        #[serde(skip_serializing_if = "is_default")]
284        fields: Option<IdlDefinedFields>,
285    },
286    Enum {
287        variants: Vec<IdlEnumVariant>,
288    },
289    Type {
290        alias: IdlType,
291    },
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
295pub struct IdlEnumVariant {
296    pub name: String,
297    #[serde(skip_serializing_if = "is_default")]
298    pub fields: Option<IdlDefinedFields>,
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
302#[serde(untagged)]
303pub enum IdlDefinedFields {
304    Named(Vec<IdlField>),
305    Tuple(Vec<IdlType>),
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
309#[serde(rename_all = "lowercase")]
310pub enum IdlArrayLen {
311    Generic(String),
312    #[serde(untagged)]
313    Value(usize),
314}
315
316/// Generic argument used in defined types - supports both v29 and v30+ formats
317#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
318#[serde(untagged)]
319pub enum IdlGenericArg {
320    /// v30+ format with explicit kind
321    WithKind(IdlGenericArgWithKind),
322    /// v29 format without kind - just type or generic reference
323    Type {
324        #[serde(rename = "type")]
325        ty: IdlType,
326    },
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
330#[serde(tag = "kind", rename_all = "lowercase")]
331pub enum IdlGenericArgWithKind {
332    Type {
333        #[serde(rename = "type")]
334        ty: IdlType,
335    },
336    Const {
337        value: String,
338    },
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
342#[serde(rename_all = "camelCase")]
343pub enum IdlDefinedTypeArg {
344    Generic(String),
345    Value(String),
346    Type(IdlType),
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
350#[serde(rename_all = "camelCase")]
351#[non_exhaustive]
352pub enum IdlType {
353    Bool,
354    U8,
355    I8,
356    U16,
357    I16,
358    U32,
359    I32,
360    F32,
361    U64,
362    I64,
363    F64,
364    U128,
365    I128,
366    U256,
367    I256,
368    Bytes,
369    String,
370    /// v30+ format
371    Pubkey,
372    /// v29 format
373    PublicKey,
374    Option(Box<IdlType>),
375    Vec(Box<IdlType>),
376    Array(Box<IdlType>, IdlArrayLen),
377    Generic(String),
378    /// v30+ format: { "defined": { "name": "...", "generics": [...] } }
379    Defined(DefinedType),
380    /// v29 format: { "definedWithTypeArgs": { "name": "...", "args": [...] } }
381    DefinedWithTypeArgs(DefinedWithTypeArgs),
382}
383
384impl IdlType {
385    /// Check if this type represents a public key (either pubkey or publicKey)
386    pub fn is_pubkey(&self) -> bool {
387        matches!(self, IdlType::Pubkey | IdlType::PublicKey)
388    }
389}
390
391/// v30+ format for defined types: { "defined": { "name": "...", "generics": [...] } }
392/// Also supports v29 simple format: { "defined": "TypeName" }
393#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
394#[serde(untagged)]
395pub enum DefinedType {
396    /// v29 simple format: just a string
397    Simple(String),
398    /// v30+ format: object with name and optional generics
399    Complex {
400        name: String,
401        #[serde(default, skip_serializing_if = "is_default")]
402        generics: Vec<IdlGenericArg>,
403    },
404}
405
406impl DefinedType {
407    /// Get the type name regardless of format
408    pub fn get_name(&self) -> &str {
409        match self {
410            DefinedType::Simple(name) => name,
411            DefinedType::Complex { name, .. } => name,
412        }
413    }
414
415    /// Get the generics if any
416    pub fn get_generics(&self) -> &[IdlGenericArg] {
417        match self {
418            DefinedType::Simple(_) => &[],
419            DefinedType::Complex { generics, .. } => generics,
420        }
421    }
422}
423
424/// v29 format for defined types with generics: { "definedWithTypeArgs": { "name": "...", "args": [...] } }
425#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
426pub struct DefinedWithTypeArgs {
427    pub name: String,
428    #[serde(default, skip_serializing_if = "is_default")]
429    pub args: Vec<IdlGenericArg>,
430}
431
432pub type IdlDiscriminator = Vec<u8>;
433
434/// Get whether the given data is the default of its type.
435fn is_default<T: Default + PartialEq>(it: &T) -> bool {
436    *it == T::default()
437}