1use crate::types::IdlSpec;
4use std::fs;
5use std::path::Path;
6
7pub fn parse_idl_file<P: AsRef<Path>>(path: P) -> Result<IdlSpec, String> {
8 let content = fs::read_to_string(&path)
9 .map_err(|e| format!("Failed to read IDL file {:?}: {}", path.as_ref(), e))?;
10
11 parse_idl_content(&content)
12}
13
14pub fn parse_idl_content(content: &str) -> Result<IdlSpec, String> {
15 serde_json::from_str(content).map_err(|e| format!("Failed to parse IDL JSON: {}", e))
16}
17
18#[cfg(test)]
19mod tests {
20 use super::*;
21 use crate::discriminator::anchor_discriminator;
22 use sha2::{Digest, Sha256};
23
24 #[test]
25 fn test_anchor_discriminator_known_values() {
26 let disc = anchor_discriminator("global:initialize");
27 assert_eq!(disc.len(), 8);
28 assert_eq!(disc, &Sha256::digest(b"global:initialize")[..8]);
29 }
30
31 #[test]
32 fn test_anchor_account_discriminator() {
33 let disc = anchor_discriminator("account:LendingMarket");
34 assert_eq!(disc.len(), 8);
35 assert_eq!(disc, &Sha256::digest(b"account:LendingMarket")[..8]);
36 }
37
38 #[test]
39 fn test_legacy_idl_parses_without_discriminator() {
40 let json = r#"{
41 "address": "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8",
42 "version": "0.3.0",
43 "name": "raydium_amm",
44 "instructions": [
45 {
46 "name": "initialize",
47 "accounts": [
48 { "name": "tokenProgram", "isMut": false, "isSigner": false }
49 ],
50 "args": [
51 { "name": "nonce", "type": "u8" }
52 ]
53 }
54 ],
55 "accounts": [
56 {
57 "name": "TargetOrders",
58 "type": {
59 "kind": "struct",
60 "fields": [
61 { "name": "owner", "type": { "array": ["u64", 4] } }
62 ]
63 }
64 }
65 ],
66 "types": [],
67 "errors": []
68 }"#;
69 let idl = parse_idl_content(json).expect("legacy IDL should parse");
70
71 assert_eq!(idl.instructions.len(), 1);
72 assert_eq!(idl.accounts.len(), 1);
73 assert!(idl.accounts[0].discriminator.is_empty());
74 assert!(idl.instructions[0].discriminator.is_empty());
75 assert!(idl.instructions[0].discriminant.is_none());
76 }
77
78 #[test]
79 fn test_legacy_instruction_computes_discriminator() {
80 let json = r#"{
81 "name": "raydium_amm",
82 "instructions": [
83 {
84 "name": "initialize",
85 "accounts": [],
86 "args": []
87 }
88 ],
89 "accounts": [],
90 "types": [],
91 "errors": []
92 }"#;
93 let idl = parse_idl_content(json).unwrap();
94 let disc = idl.instructions[0].get_discriminator();
95
96 assert_eq!(disc.len(), 8);
97 let expected = anchor_discriminator("global:initialize");
98 assert_eq!(disc, expected);
99 }
100
101 #[test]
102 fn test_legacy_account_computes_discriminator() {
103 let json = r#"{
104 "name": "test",
105 "instructions": [],
106 "accounts": [
107 {
108 "name": "LendingMarket",
109 "type": { "kind": "struct", "fields": [] }
110 }
111 ],
112 "types": [],
113 "errors": []
114 }"#;
115 let idl = parse_idl_content(json).unwrap();
116 let disc = idl.accounts[0].get_discriminator();
117
118 assert_eq!(disc.len(), 8);
119 let expected = anchor_discriminator("account:LendingMarket");
120 assert_eq!(disc, expected);
121 }
122
123 #[test]
124 fn test_explicit_discriminator_not_overridden() {
125 let json = r#"{
126 "name": "test",
127 "instructions": [
128 {
129 "name": "transfer",
130 "discriminator": [1, 2, 3, 4, 5, 6, 7, 8],
131 "accounts": [],
132 "args": []
133 }
134 ],
135 "accounts": [
136 {
137 "name": "TokenAccount",
138 "discriminator": [10, 20, 30, 40, 50, 60, 70, 80]
139 }
140 ],
141 "types": [],
142 "errors": []
143 }"#;
144 let idl = parse_idl_content(json).unwrap();
145
146 assert_eq!(
147 idl.instructions[0].get_discriminator(),
148 vec![1, 2, 3, 4, 5, 6, 7, 8]
149 );
150 assert_eq!(
151 idl.accounts[0].get_discriminator(),
152 vec![10, 20, 30, 40, 50, 60, 70, 80]
153 );
154 }
155
156 #[test]
157 fn test_steel_discriminant_still_works() {
158 let json = r#"{
159 "name": "test",
160 "instructions": [
161 {
162 "name": "CreateMetadataAccount",
163 "accounts": [],
164 "args": [],
165 "discriminant": { "type": "u8", "value": 0 }
166 },
167 {
168 "name": "UpdateMetadataAccount",
169 "accounts": [],
170 "args": [],
171 "discriminant": { "type": "u8", "value": 1 }
172 }
173 ],
174 "accounts": [],
175 "types": [],
176 "errors": []
177 }"#;
178 let idl = parse_idl_content(json).unwrap();
179
180 assert_eq!(idl.instructions[0].get_discriminator(), vec![0]);
181 assert_eq!(idl.instructions[1].get_discriminator(), vec![1]);
182 }
183
184 #[test]
185 fn test_legacy_event_computes_discriminator() {
186 let json = r#"{
187 "name": "test",
188 "instructions": [],
189 "accounts": [],
190 "types": [],
191 "events": [
192 { "name": "TransferEvent" }
193 ],
194 "errors": []
195 }"#;
196 let idl = parse_idl_content(json).unwrap();
197 let disc = idl.events[0].get_discriminator();
198
199 assert_eq!(disc.len(), 8);
200 let expected = anchor_discriminator("event:TransferEvent");
201 assert_eq!(disc, expected);
202 }
203
204 #[test]
205 fn test_is_mut_is_signer_aliases() {
206 let json = r#"{
207 "name": "test",
208 "instructions": [
209 {
210 "name": "do_thing",
211 "accounts": [
212 { "name": "payer", "isMut": true, "isSigner": true },
213 { "name": "dest", "writable": true, "signer": false }
214 ],
215 "args": []
216 }
217 ],
218 "accounts": [],
219 "types": [],
220 "errors": []
221 }"#;
222 let idl = parse_idl_content(json).unwrap();
223 let accounts = &idl.instructions[0].accounts;
224
225 assert!(accounts[0].is_mut);
226 assert!(accounts[0].is_signer);
227 assert!(accounts[1].is_mut);
228 assert!(!accounts[1].is_signer);
229 }
230
231 #[test]
232 fn test_constants() {
233 let json = r#"{
234 "address": "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo",
235 "metadata": {
236 "name": "lb_clmm",
237 "version": "0.11.0",
238 "spec": "0.1.0",
239 "description": "Created with Anchor"
240 },
241 "instructions": [],
242 "accounts": [],
243 "types": [],
244 "events": [],
245 "errors": [],
246 "constants": [
247 {
248 "name": "BASIS_POINT_MAX",
249 "type": "i32",
250 "value": "10000"
251 },
252 {
253 "name": "MAX_BIN_PER_ARRAY",
254 "type": "u64",
255 "value": "70"
256 }
257 ]
258 }"#;
259 let idl = parse_idl_content(json).expect("IDL with constants should parse");
260
261 assert_eq!(idl.constants.len(), 2);
262 assert_eq!(idl.constants[0].name, "BASIS_POINT_MAX");
263 assert_eq!(idl.constants[0].value, "10000");
264 assert_eq!(idl.constants[1].name, "MAX_BIN_PER_ARRAY");
265 assert_eq!(idl.constants[1].value, "70");
266 }
267}