1use anchor_lang::prelude::*;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct DerivedPda {
21 pub key: Pubkey,
23 pub bump: u8,
25 pub seed_strings: Vec<String>,
27 pub seeds: Vec<Vec<u8>>,
29}
30
31impl DerivedPda {
32 pub fn verify(&self, program_id: &Pubkey) -> bool {
34 let seed_refs: Vec<&[u8]> = self.seeds.iter().map(|s| s.as_slice()).collect();
35 let (expected_key, expected_bump) = Pubkey::find_program_address(&seed_refs, program_id);
36 self.key == expected_key && self.bump == expected_bump
37 }
38}
39
40pub fn seed_to_string<T: AsRef<[u8]>>(seed: T) -> String {
42 let bytes = seed.as_ref();
44
45 if bytes.iter().all(|&b| b.is_ascii_graphic() || b == b' ')
47 && let Ok(s) = std::str::from_utf8(bytes)
48 {
49 return s.to_string();
50 }
51
52 if bytes.len() == 32
54 && let Ok(pubkey) = Pubkey::try_from(bytes)
55 {
56 return pubkey.to_string();
57 }
58
59 hex::encode(bytes)
61}
62
63pub fn find_pda_with_bump_and_strings(seeds: &[&[u8]], program_id: &Pubkey) -> DerivedPda {
75 let seed_bytes: Vec<&[u8]> = seeds.iter().map(|s| s.as_ref()).collect();
77
78 let (pubkey, bump) = Pubkey::find_program_address(&seed_bytes, program_id);
80
81 let seed_strings: Vec<String> = seeds.iter().map(seed_to_string).collect();
83
84 let seeds_owned: Vec<Vec<u8>> = seeds.iter().map(|s| s.as_ref().to_vec()).collect();
86
87 DerivedPda {
88 key: pubkey,
89 bump,
90 seed_strings,
91 seeds: seeds_owned,
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_string_seed() {
101 let program_id = Pubkey::new_unique();
102 let seed = "test_seed";
103
104 let (pda, bump) = Pubkey::find_program_address(&[seed.as_bytes()], &program_id);
105
106 let (pda2, bump2) = Pubkey::find_program_address(&[seed.as_bytes()], &program_id);
108 assert_eq!(pda, pda2);
109 assert_eq!(bump, bump2);
110
111 assert!(!pda.is_on_curve());
113 }
114
115 #[test]
116 fn test_multiple_string_seeds() {
117 let program_id = Pubkey::new_unique();
118 let seed1 = "user";
119 let seed2 = "profile";
120 let seeds: Vec<&[u8]> = vec![seed1.as_bytes(), seed2.as_bytes()];
121
122 let (pda, _bump) = Pubkey::find_program_address(&seeds, &program_id);
123
124 let seeds_reversed: Vec<&[u8]> = vec![seed2.as_bytes(), seed1.as_bytes()];
126 let (pda_reversed, _) = Pubkey::find_program_address(&seeds_reversed, &program_id);
127 assert_ne!(pda, pda_reversed);
128 }
129
130 #[test]
131 fn test_pubkey_seed() {
132 let program_id = Pubkey::new_unique();
133 let user_pubkey = Pubkey::new_unique();
134 let seeds: Vec<&[u8]> = vec![user_pubkey.as_ref()];
135
136 let (pda, bump) = Pubkey::find_program_address(&seeds, &program_id);
137
138 let (pda2, bump2) = Pubkey::find_program_address(&seeds, &program_id);
140 assert_eq!(pda, pda2);
141 assert_eq!(bump, bump2);
142 }
143
144 #[test]
145 fn test_mixed_seeds() {
146 let program_id = Pubkey::new_unique();
147 let prefix = "vault";
148 let owner = Pubkey::new_unique();
149 let id: u64 = 12345;
150 let id_bytes = id.to_le_bytes();
151
152 let seeds: Vec<&[u8]> = vec![prefix.as_bytes(), owner.as_ref(), &id_bytes];
153
154 let (pda, _bump) = Pubkey::find_program_address(&seeds, &program_id);
155 assert!(!pda.is_on_curve());
156 }
157
158 #[test]
159 fn test_byte_array_seed() {
160 let program_id = Pubkey::new_unique();
161 let bytes: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
162 let seeds: Vec<&[u8]> = vec![&bytes];
163
164 let (pda, _bump) = Pubkey::find_program_address(&seeds, &program_id);
165 assert!(!pda.is_on_curve());
166 }
167
168 #[test]
169 fn test_slice_seed() {
170 let program_id = Pubkey::new_unique();
171 let vec_bytes = vec![9, 8, 7, 6, 5, 4, 3, 2, 1];
172 let seeds: Vec<&[u8]> = vec![&vec_bytes];
173
174 let (pda, _bump) = Pubkey::find_program_address(&seeds, &program_id);
175 assert!(!pda.is_on_curve());
176 }
177
178 #[test]
179 fn test_find_pda_with_strings() {
180 let program_id = Pubkey::new_unique();
181 let seed1 = "metadata";
182 let seed2 = Pubkey::new_unique();
183 let seed3: u32 = 42;
184 let seed3_bytes = seed3.to_le_bytes();
185
186 let seeds: Vec<&[u8]> = vec![seed1.as_bytes(), seed2.as_ref(), &seed3_bytes];
187
188 let derived_pda = find_pda_with_bump_and_strings(&seeds, &program_id);
189
190 assert_eq!(derived_pda.seed_strings.len(), 3);
192 assert_eq!(derived_pda.seed_strings[0], "metadata");
193 assert_eq!(derived_pda.seed_strings[1], seed2.to_string());
194
195 let (pda2, bump2) = Pubkey::find_program_address(&seeds, &program_id);
197 assert_eq!(derived_pda.key, pda2);
198 assert_eq!(derived_pda.bump, bump2);
199
200 assert_eq!(derived_pda.seeds.len(), 3);
202 assert_eq!(derived_pda.seeds[0], seed1.as_bytes());
203 assert_eq!(derived_pda.seeds[1], seed2.as_ref());
204 assert_eq!(derived_pda.seeds[2], seed3_bytes.as_ref());
205
206 let seed_refs: Vec<&[u8]> = derived_pda.seeds.iter().map(|s| s.as_slice()).collect();
208 let (expected_key, expected_bump) = Pubkey::find_program_address(&seed_refs, &program_id);
209 assert_eq!(derived_pda.key, expected_key);
210 assert_eq!(derived_pda.bump, expected_bump);
211
212 assert!(derived_pda.verify(&program_id));
214 }
215
216 #[test]
217 fn test_empty_seeds() {
218 let program_id = Pubkey::new_unique();
219 let seeds: Vec<&[u8]> = vec![];
220
221 let (pda, _bump) = Pubkey::find_program_address(&seeds, &program_id);
222
223 assert!(!pda.is_on_curve());
225 }
226
227 #[test]
228 fn test_max_seed_length() {
229 let program_id = Pubkey::new_unique();
230 let max_seed = vec![0u8; 32];
232 let seeds: Vec<&[u8]> = vec![&max_seed];
233
234 let (pda, _bump) = Pubkey::find_program_address(&seeds, &program_id);
235 assert!(!pda.is_on_curve());
236 }
237
238 #[test]
239 fn test_different_programs_same_seeds() {
240 let program_id1 = Pubkey::new_unique();
241 let program_id2 = Pubkey::new_unique();
242 let seed = "same_seed";
243 let seeds: Vec<&[u8]> = vec![seed.as_bytes()];
244
245 let (pda1, _) = Pubkey::find_program_address(&seeds, &program_id1);
246 let (pda2, _) = Pubkey::find_program_address(&seeds, &program_id2);
247
248 assert_ne!(pda1, pda2);
250 }
251
252 #[test]
253 fn test_bump_determinism() {
254 let program_id = Pubkey::new_unique();
255 let seed = "deterministic";
256 let seeds: Vec<&[u8]> = vec![seed.as_bytes()];
257
258 let results: Vec<(Pubkey, u8)> = (0..10)
260 .map(|_| Pubkey::find_program_address(&seeds, &program_id))
261 .collect();
262
263 for result in &results[1..] {
265 assert_eq!(result.0, results[0].0);
266 assert_eq!(result.1, results[0].1);
267 }
268 }
269
270 #[test]
271 fn test_known_pda() {
272 let program_id = Pubkey::default(); let seed = "test";
275 let seeds: Vec<&[u8]> = vec![seed.as_bytes()];
276
277 let (pda, bump) = Pubkey::find_program_address(&seeds, &program_id);
278
279 let (expected_pda, expected_bump) =
281 Pubkey::find_program_address(&[seed.as_bytes()], &program_id);
282
283 assert_eq!(pda, expected_pda);
284 assert_eq!(bump, expected_bump);
285 }
286
287 #[test]
288 fn test_derived_pda_verify() {
289 let program_id = Pubkey::new_unique();
290 let seed1 = "vault";
291 let seed2 = Pubkey::new_unique();
292 let seeds: Vec<&[u8]> = vec![seed1.as_ref(), seed2.as_ref()];
293
294 let derived_pda = find_pda_with_bump_and_strings(&seeds, &program_id);
295
296 assert!(derived_pda.verify(&program_id));
298
299 let different_program = Pubkey::new_unique();
301 assert!(!derived_pda.verify(&different_program));
302
303 let seed_refs: Vec<&[u8]> = derived_pda.seeds.iter().map(|s| s.as_slice()).collect();
305 let (manual_key, manual_bump) = Pubkey::find_program_address(&seed_refs, &program_id);
306 assert_eq!(derived_pda.key, manual_key);
307 assert_eq!(derived_pda.bump, manual_bump);
308 }
309
310 #[test]
311 fn test_derived_pda_fields() {
312 let program_id = Pubkey::new_unique();
313 let string_seed = "config";
314 let pubkey_seed = Pubkey::new_unique();
315 let byte_seed: u64 = 999;
316 let byte_seed_bytes = byte_seed.to_le_bytes();
317
318 let seeds: Vec<&[u8]> = vec![
319 string_seed.as_bytes(),
320 pubkey_seed.as_ref(),
321 &byte_seed_bytes,
322 ];
323 let derived_pda = find_pda_with_bump_and_strings(&seeds, &program_id);
324
325 assert!(!derived_pda.key.is_on_curve());
327 assert_eq!(derived_pda.seed_strings.len(), 3);
329 assert_eq!(derived_pda.seeds.len(), 3);
330
331 assert_eq!(derived_pda.seed_strings[0], "config");
333 assert_eq!(derived_pda.seed_strings[1], pubkey_seed.to_string());
334 assert_eq!(derived_pda.seed_strings[2], hex::encode(byte_seed_bytes));
336
337 assert_eq!(derived_pda.seeds[0], string_seed.as_bytes());
339 assert_eq!(derived_pda.seeds[1], pubkey_seed.as_ref());
340 assert_eq!(derived_pda.seeds[2], byte_seed_bytes.as_ref());
341
342 assert!(derived_pda.verify(&program_id));
344 }
345
346 #[test]
347 fn test_explicit_miner_pda_example() {
348 let program_id = Pubkey::new_unique(); let replica_quarry = Pubkey::new_unique();
351 let merge_miner = Pubkey::new_unique();
352
353 let (expected_miner_pda, expected_bump) = Pubkey::find_program_address(
355 &[b"Miner", replica_quarry.as_ref(), merge_miner.as_ref()],
356 &program_id,
357 );
358
359 let seeds: Vec<&[u8]> = vec![b"Miner", replica_quarry.as_ref(), merge_miner.as_ref()];
361 let derived_pda = find_pda_with_bump_and_strings(&seeds, &program_id);
362
363 assert_eq!(derived_pda.key, expected_miner_pda);
365 assert_eq!(derived_pda.bump, expected_bump);
366
367 assert_eq!(derived_pda.seed_strings[0], "Miner");
369 assert_eq!(derived_pda.seed_strings[1], replica_quarry.to_string());
370 assert_eq!(derived_pda.seed_strings[2], merge_miner.to_string());
371
372 assert_eq!(derived_pda.seeds[0], b"Miner");
374 assert_eq!(derived_pda.seeds[1], replica_quarry.as_ref());
375 assert_eq!(derived_pda.seeds[2], merge_miner.as_ref());
376 }
377
378 #[test]
379 fn test_explicit_vault_pda_example() {
380 let program_id = Pubkey::new_unique();
382 let authority = Pubkey::new_unique();
383 let token_mint = Pubkey::new_unique();
384 let vault_id: u64 = 1;
385
386 let (expected_vault_pda, expected_bump) = Pubkey::find_program_address(
388 &[
389 b"vault",
390 authority.as_ref(),
391 token_mint.as_ref(),
392 &vault_id.to_le_bytes(),
393 ],
394 &program_id,
395 );
396
397 let vault_id_bytes = vault_id.to_le_bytes();
399 let seeds: Vec<&[u8]> = vec![
400 b"vault",
401 authority.as_ref(),
402 token_mint.as_ref(),
403 &vault_id_bytes,
404 ];
405 let derived_pda = find_pda_with_bump_and_strings(&seeds, &program_id);
406
407 assert_eq!(derived_pda.key, expected_vault_pda);
409 assert_eq!(derived_pda.bump, expected_bump);
410 assert!(derived_pda.verify(&program_id));
411 }
412
413 #[test]
414 fn test_explicit_metadata_pda_example() {
415 let metadata_program_id = Pubkey::new_unique(); let mint_pubkey = Pubkey::new_unique();
418
419 let (expected_metadata_pda, expected_bump) = Pubkey::find_program_address(
421 &[
422 b"metadata",
423 metadata_program_id.as_ref(),
424 mint_pubkey.as_ref(),
425 ],
426 &metadata_program_id,
427 );
428
429 let seeds: Vec<&[u8]> = vec![
431 b"metadata",
432 metadata_program_id.as_ref(),
433 mint_pubkey.as_ref(),
434 ];
435 let derived_pda = find_pda_with_bump_and_strings(&seeds, &metadata_program_id);
436
437 assert_eq!(derived_pda.key, expected_metadata_pda);
439 assert_eq!(derived_pda.bump, expected_bump);
440
441 assert_eq!(derived_pda.seeds[0], b"metadata");
443 assert_eq!(derived_pda.seeds[1], metadata_program_id.as_ref());
444 assert_eq!(derived_pda.seeds[2], mint_pubkey.as_ref());
445 }
446
447 #[test]
448 fn test_explicit_associated_token_account_example() {
449 let token_program_id = Pubkey::new_unique(); let associated_token_program_id = Pubkey::new_unique(); let wallet = Pubkey::new_unique();
453 let mint = Pubkey::new_unique();
454
455 let (expected_ata, _expected_bump) = Pubkey::find_program_address(
457 &[wallet.as_ref(), token_program_id.as_ref(), mint.as_ref()],
458 &associated_token_program_id,
459 );
460
461 let seeds: Vec<&[u8]> = vec![wallet.as_ref(), token_program_id.as_ref(), mint.as_ref()];
463 let derived_pda = find_pda_with_bump_and_strings(&seeds, &associated_token_program_id);
464
465 assert_eq!(derived_pda.key, expected_ata);
467
468 let (reconstructed_ata, reconstructed_bump) = Pubkey::find_program_address(
470 &[
471 &derived_pda.seeds[0],
472 &derived_pda.seeds[1],
473 &derived_pda.seeds[2],
474 ],
475 &associated_token_program_id,
476 );
477 assert_eq!(reconstructed_ata, expected_ata);
478 assert_eq!(reconstructed_bump, derived_pda.bump);
479 }
480
481 #[test]
482 fn test_explicit_escrow_pda_example() {
483 let program_id = Pubkey::new_unique();
485 let initializer = Pubkey::new_unique();
486 let escrow_seed = b"escrow";
487 let escrow_id: u32 = 12345;
488 let timestamp: i64 = 1234567890;
489
490 let (expected_escrow_pda, expected_bump) = Pubkey::find_program_address(
492 &[
493 escrow_seed,
494 initializer.as_ref(),
495 &escrow_id.to_le_bytes(),
496 ×tamp.to_le_bytes(),
497 ],
498 &program_id,
499 );
500
501 let escrow_id_bytes = escrow_id.to_le_bytes();
503 let timestamp_bytes = timestamp.to_le_bytes();
504 let seeds: Vec<&[u8]> = vec![
505 b"escrow",
506 initializer.as_ref(),
507 &escrow_id_bytes,
508 ×tamp_bytes,
509 ];
510
511 let (pda_basic, bump_basic) = Pubkey::find_program_address(&seeds, &program_id);
513 assert_eq!(pda_basic, expected_escrow_pda);
514 assert_eq!(bump_basic, expected_bump);
515
516 let derived_pda = find_pda_with_bump_and_strings(&seeds, &program_id);
518 assert_eq!(derived_pda.key, expected_escrow_pda);
519 assert_eq!(derived_pda.bump, expected_bump);
520
521 assert_eq!(derived_pda.seeds[0], escrow_seed);
523 assert_eq!(derived_pda.seeds[1], initializer.as_ref());
524 assert_eq!(derived_pda.seeds[2], escrow_id.to_le_bytes().as_ref());
525 assert_eq!(derived_pda.seeds[3], timestamp.to_le_bytes().as_ref());
526
527 let (manual_check, manual_bump) = Pubkey::find_program_address(
529 &[
530 &derived_pda.seeds[0],
531 &derived_pda.seeds[1],
532 &derived_pda.seeds[2],
533 &derived_pda.seeds[3],
534 ],
535 &program_id,
536 );
537 assert_eq!(manual_check, expected_escrow_pda);
538 assert_eq!(manual_bump, expected_bump);
539 }
540}