1mod generated;
2
3pub use generated::programs::SHIELD_ID as ID;
4pub use generated::*;
5use solana_hash::Hash;
6use solana_instruction::Instruction;
7use solana_keypair::Keypair;
8use solana_pubkey::{Pubkey, PUBKEY_BYTES};
9use solana_rent::Rent;
10use solana_system_interface::instruction as system_instruction;
11use solana_transaction::Transaction;
12use std::str::FromStr;
13
14#[cfg(feature = "token-extensions")]
15use spl_associated_token_account::instruction::create_associated_token_account;
16
17#[cfg(feature = "token-extensions")]
18use spl_token_2022_interface::{
19 extension::metadata_pointer::instruction::initialize as initialize_metadata_pointer,
20 instruction::{initialize_mint2, mint_to},
21 ID as TOKEN_22_PROGRAM_ID,
22};
23
24#[cfg(feature = "token-extensions")]
25use spl_token_metadata_interface::instruction::initialize as initialize_metadata;
26
27#[derive(Debug, thiserror::Error)]
28pub enum ParseError {
29 #[error("Invalid permission strategy")]
30 InvalidStrategy,
31 #[error("Invalid kind")]
32 InvalidKind,
33 #[error("No mint")]
34 NoMint,
35 #[error("Invalid data")]
36 InvalidData,
37 #[error("IO error occurred: {0}")]
38 Io(#[from] std::io::Error),
39}
40
41impl FromStr for generated::types::PermissionStrategy {
42 type Err = ParseError;
43
44 fn from_str(s: &str) -> Result<Self, Self::Err> {
45 match s.to_lowercase().as_str() {
46 "allow" => Ok(generated::types::PermissionStrategy::Allow),
47 "deny" => Ok(generated::types::PermissionStrategy::Deny),
48 _ => Err(ParseError::InvalidStrategy),
49 }
50 }
51}
52
53impl TryFrom<u8> for generated::types::PermissionStrategy {
54 type Error = ParseError;
55
56 fn try_from(value: u8) -> Result<Self, Self::Error> {
57 match value {
58 0 => Ok(generated::types::PermissionStrategy::Deny),
59 1 => Ok(generated::types::PermissionStrategy::Allow),
60 _ => Err(ParseError::InvalidStrategy),
61 }
62 }
63}
64
65impl TryFrom<u8> for generated::types::Kind {
66 type Error = ParseError;
67
68 fn try_from(value: u8) -> Result<Self, Self::Error> {
69 match value {
70 0 => Ok(generated::types::Kind::Policy),
71 1 => Ok(generated::types::Kind::PolicyV2),
72 _ => Err(ParseError::InvalidKind),
73 }
74 }
75}
76
77pub trait PolicyTrait {
78 const LEN: usize;
79 fn current_identities_len(&self) -> u32;
80 fn try_deserialize_identities(data: &[u8]) -> Result<Vec<Pubkey>, ParseError>;
81 fn try_kind(&self) -> Result<generated::types::Kind, ParseError>;
82 fn try_strategy(&self) -> Result<generated::types::PermissionStrategy, ParseError>;
83 fn try_mint(&self) -> Result<Pubkey, ParseError>;
84 fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error>
85 where
86 Self: Sized;
87}
88
89impl PolicyTrait for generated::accounts::Policy {
90 const LEN: usize = generated::accounts::Policy::LEN;
91
92 fn try_mint(&self) -> Result<Pubkey, ParseError> {
93 Err(ParseError::NoMint)
94 }
95
96 fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
97 generated::accounts::Policy::from_bytes(&data[..Self::LEN])
98 }
99
100 fn current_identities_len(&self) -> u32 {
101 u32::from_le_bytes(self.identities_len)
102 }
103
104 fn try_deserialize_identities(data: &[u8]) -> Result<Vec<Pubkey>, ParseError> {
105 let identities_data = &data[Self::LEN..];
106
107 if identities_data.len() % PUBKEY_BYTES != 0 {
108 return Err(ParseError::InvalidData);
109 }
110
111 let identities = identities_data
112 .chunks_exact(PUBKEY_BYTES)
113 .map(Pubkey::try_from)
114 .collect::<Result<Vec<_>, _>>()
115 .map_err(|_| ParseError::InvalidData)?;
116
117 Ok(identities)
118 }
119
120 fn try_kind(&self) -> Result<generated::types::Kind, ParseError> {
121 generated::types::Kind::try_from(self.kind)
122 }
123
124 fn try_strategy(&self) -> Result<generated::types::PermissionStrategy, ParseError> {
125 generated::types::PermissionStrategy::try_from(self.strategy)
126 }
127}
128
129impl PolicyTrait for generated::accounts::PolicyV2 {
130 const LEN: usize = generated::accounts::PolicyV2::LEN;
131
132 fn try_mint(&self) -> Result<Pubkey, ParseError> {
133 Ok(self.mint)
134 }
135
136 fn current_identities_len(&self) -> u32 {
137 u32::from_le_bytes(self.identities_len)
138 }
139
140 fn try_deserialize_identities(data: &[u8]) -> Result<Vec<Pubkey>, ParseError> {
141 let identities_data = &data[Self::LEN..];
142
143 if identities_data.len() % PUBKEY_BYTES != 0 {
144 return Err(ParseError::InvalidData);
145 }
146
147 let identities = identities_data
148 .chunks_exact(PUBKEY_BYTES)
149 .map(Pubkey::try_from)
150 .collect::<Result<Vec<_>, _>>()
151 .map_err(|_| ParseError::InvalidData)?;
152
153 Ok(identities)
154 }
155
156 fn try_kind(&self) -> Result<generated::types::Kind, ParseError> {
157 generated::types::Kind::try_from(self.kind)
158 }
159
160 fn try_strategy(&self) -> Result<generated::types::PermissionStrategy, ParseError> {
161 generated::types::PermissionStrategy::try_from(self.strategy)
162 }
163
164 fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
165 generated::accounts::PolicyV2::from_bytes(&data[..Self::LEN])
166 }
167}
168
169#[derive(Clone, Debug, Default)]
177pub struct CreateAccountBuilder<'a> {
178 payer: Option<&'a Pubkey>,
179 account: Option<&'a Pubkey>,
180 space: Option<usize>,
181 owner: Option<&'a Pubkey>,
182 rent: Option<usize>,
183}
184
185impl<'a> CreateAccountBuilder<'a> {
186 pub fn build() -> Self {
187 Self::default()
188 }
189
190 #[inline(always)]
192 pub fn payer(&mut self, payer: &'a Pubkey) -> &mut Self {
193 self.payer = Some(payer);
194 self
195 }
196
197 #[inline(always)]
199 pub fn account(&mut self, account: &'a Pubkey) -> &mut Self {
200 self.account = Some(account);
201 self
202 }
203
204 #[inline(always)]
206 pub fn space(&mut self, space: usize) -> &mut Self {
207 self.space = Some(space);
208 self
209 }
210
211 #[inline(always)]
213 pub fn owner(&mut self, owner: &'a Pubkey) -> &mut Self {
214 self.owner = Some(owner);
215 self
216 }
217
218 #[inline(always)]
220 pub fn rent(&mut self, rent: usize) -> &mut Self {
221 self.rent = Some(rent);
222 self
223 }
224
225 pub fn instruction(&self) -> Instruction {
226 let space = self.space.expect("space is not set");
227 let lamports = Rent::default().minimum_balance(self.rent.expect("rent is not set"));
228
229 system_instruction::create_account(
230 self.payer.expect("payer is not set"),
231 self.account.expect("mint is not set"),
232 lamports,
233 u64::try_from(space).expect("space is too large"),
234 self.owner.expect("owner is not set"),
235 )
236 }
237}
238
239#[cfg(feature = "token-extensions")]
240pub struct InitializeMint2Builder<'a> {
241 mint: Option<&'a Pubkey>,
242 mint_authority: Option<&'a Pubkey>,
243 freeze_authority: Option<&'a Pubkey>,
244 token_program: Option<&'a Pubkey>,
245}
246
247#[cfg(feature = "token-extensions")]
248impl<'a> InitializeMint2Builder<'a> {
249 pub fn build() -> Self {
250 Self {
251 mint: None,
252 mint_authority: None,
253 freeze_authority: None,
254 token_program: None,
255 }
256 }
257
258 #[inline(always)]
260 pub fn mint(&mut self, mint: &'a Pubkey) -> &mut Self {
261 self.mint = Some(mint);
262 self
263 }
264
265 #[inline(always)]
267 pub fn mint_authority(&mut self, mint_authority: &'a Pubkey) -> &mut Self {
268 self.mint_authority = Some(mint_authority);
269 self
270 }
271
272 #[inline(always)]
274 pub fn freeze_authority(&mut self, freeze_authority: &'a Pubkey) -> &mut Self {
275 self.freeze_authority = Some(freeze_authority);
276 self
277 }
278
279 #[inline(always)]
281 pub fn token_program(&mut self, token_program: &'a Pubkey) -> &mut Self {
282 self.token_program = Some(token_program);
283 self
284 }
285
286 pub fn instruction(&self) -> Instruction {
287 initialize_mint2(
288 self.token_program.unwrap_or(&TOKEN_22_PROGRAM_ID),
289 self.mint.expect("mint is not set"),
290 self.mint_authority.expect("mint_authority is not set"),
291 self.freeze_authority,
292 0,
293 )
294 .expect("Failed to create initialize_mint2 instruction")
295 }
296}
297
298#[cfg(feature = "token-extensions")]
299pub struct MetadataPointerInitializeBuilder<'a> {
300 token_program: Option<&'a Pubkey>,
301 mint: Option<&'a Pubkey>,
302 metadata: Option<Pubkey>,
303 authority: Option<Pubkey>,
304}
305
306#[cfg(feature = "token-extensions")]
307impl<'a> MetadataPointerInitializeBuilder<'a> {
308 pub fn build() -> Self {
309 Self {
310 token_program: None,
311 mint: None,
312 metadata: None,
313 authority: None,
314 }
315 }
316
317 #[inline(always)]
319 pub fn token_program(&mut self, token_program: &'a Pubkey) -> &mut Self {
320 self.token_program = Some(token_program);
321 self
322 }
323
324 #[inline(always)]
326 pub fn mint(&mut self, mint: &'a Pubkey) -> &mut Self {
327 self.mint = Some(mint);
328 self
329 }
330
331 #[inline(always)]
333 pub fn metadata(&mut self, metadata: Pubkey) -> &mut Self {
334 self.metadata = Some(metadata);
335 self
336 }
337
338 #[inline(always)]
340 pub fn authority(&mut self, authority: Pubkey) -> &mut Self {
341 self.authority = Some(authority);
342 self
343 }
344
345 pub fn instruction(&self) -> Instruction {
346 initialize_metadata_pointer(
347 self.token_program.unwrap_or(&TOKEN_22_PROGRAM_ID),
348 self.mint.expect("mint is not set"),
349 self.authority,
350 self.metadata,
351 )
352 .expect("Failed to create metadata pointer initialize instruction")
353 }
354}
355
356#[cfg(feature = "token-extensions")]
357pub struct CreateAsscoiatedTokenAccountBuilder<'a> {
358 token_program: Option<&'a Pubkey>,
359 mint: Option<&'a Pubkey>,
360 owner: Option<&'a Pubkey>,
361 payer: Option<&'a Pubkey>,
362}
363
364#[cfg(feature = "token-extensions")]
365impl<'a> CreateAsscoiatedTokenAccountBuilder<'a> {
366 pub fn build() -> Self {
367 Self {
368 token_program: None,
369 mint: None,
370 owner: None,
371 payer: None,
372 }
373 }
374
375 #[inline(always)]
376 pub fn payer(&mut self, payer: &'a Pubkey) -> &mut Self {
377 self.payer = Some(payer);
378 self
379 }
380
381 #[inline(always)]
383 pub fn token_program(&mut self, token_program: &'a Pubkey) -> &mut Self {
384 self.token_program = Some(token_program);
385 self
386 }
387
388 #[inline(always)]
390 pub fn mint(&mut self, mint: &'a Pubkey) -> &mut Self {
391 self.mint = Some(mint);
392 self
393 }
394
395 #[inline(always)]
397 pub fn owner(&mut self, owner: &'a Pubkey) -> &mut Self {
398 self.owner = Some(owner);
399 self
400 }
401
402 pub fn instruction(&self) -> Instruction {
403 let owner = self.owner.expect("owner is not set");
404 let mint = self.mint.expect("mint is not set");
405 let payer = self.payer.expect("payer is not set");
406 let token_program = self.token_program.unwrap_or(&TOKEN_22_PROGRAM_ID);
407
408 create_associated_token_account(payer, owner, mint, token_program)
409 }
410}
411
412#[cfg(feature = "token-extensions")]
413pub struct TokenExtensionsMintToBuilder<'a> {
414 token_program: Option<&'a Pubkey>,
415 mint: Option<&'a Pubkey>,
416 account: Option<&'a Pubkey>,
417 owner: Option<&'a Pubkey>,
418 signers: Vec<&'a Pubkey>,
419 amount: u64,
420}
421
422#[cfg(feature = "token-extensions")]
423impl<'a> TokenExtensionsMintToBuilder<'a> {
424 pub fn build() -> Self {
425 Self {
426 token_program: None,
427 mint: None,
428 account: None,
429 owner: None,
430 signers: Vec::new(),
431 amount: 1,
432 }
433 }
434
435 #[inline(always)]
437 pub fn token_program(&mut self, token_program: &'a Pubkey) -> &mut Self {
438 self.token_program = Some(token_program);
439 self
440 }
441
442 #[inline(always)]
444 pub fn mint(&mut self, mint: &'a Pubkey) -> &mut Self {
445 self.mint = Some(mint);
446 self
447 }
448
449 #[inline(always)]
451 pub fn account(&mut self, account: &'a Pubkey) -> &mut Self {
452 self.account = Some(account);
453 self
454 }
455
456 #[inline(always)]
458 pub fn owner(&mut self, owner: &'a Pubkey) -> &mut Self {
459 self.owner = Some(owner);
460 self
461 }
462
463 #[inline(always)]
465 pub fn add_signer(&mut self, signer: &'a Pubkey) -> &mut Self {
466 self.signers.push(signer);
467 self
468 }
469
470 #[inline(always)]
472 pub fn amount(&mut self, amount: u64) -> &mut Self {
473 self.amount = amount;
474 self
475 }
476
477 pub fn instruction(&self) -> Instruction {
478 mint_to(
479 self.token_program.unwrap_or(&TOKEN_22_PROGRAM_ID),
480 self.mint.expect("mint is not set"),
481 self.account.expect("account is not set"),
482 self.owner.expect("owner is not set"),
483 &self.signers,
484 self.amount,
485 )
486 .expect("Failed to create mint to instruction")
487 }
488}
489
490pub struct TransactionBuilder<'a> {
491 instructions: Vec<Instruction>,
492 signers: Vec<&'a Keypair>,
493 payer: Option<&'a Pubkey>,
494 recent_blockhash: Option<Hash>,
495}
496
497impl<'a> TransactionBuilder<'a> {
498 pub fn build() -> Self {
499 Self {
500 instructions: Vec::new(),
501 signers: Vec::new(),
502 payer: None,
503 recent_blockhash: None,
504 }
505 }
506
507 #[inline(always)]
509 pub fn instructions(&mut self, instructions: Vec<Instruction>) -> &mut Self {
510 self.instructions = instructions;
511 self
512 }
513
514 #[inline(always)]
516 pub fn instruction(&mut self, instruction: Instruction) -> &mut Self {
517 self.instructions.push(instruction);
518 self
519 }
520
521 #[inline(always)]
523 pub fn signer(&mut self, signer: &'a Keypair) -> &mut Self {
524 self.signers.push(signer);
525 self
526 }
527
528 #[inline(always)]
530 pub fn payer(&mut self, payer: &'a Pubkey) -> &mut Self {
531 self.payer = Some(payer);
532 self
533 }
534
535 #[inline(always)]
537 pub fn recent_blockhash(&mut self, recent_blockhash: Hash) -> &mut Self {
538 self.recent_blockhash = Some(recent_blockhash);
539 self
540 }
541
542 pub fn transaction(&self) -> Transaction {
544 Transaction::new_signed_with_payer(
545 &self.instructions,
546 self.payer,
547 &self.signers,
548 self.recent_blockhash.expect("recent blockhash is not set"),
549 )
550 }
551}
552
553#[cfg(feature = "token-extensions")]
554pub struct InitializeMetadataBuilder<'a> {
555 token_program: Option<&'a Pubkey>,
556 mint: Option<&'a Pubkey>,
557 owner: Option<&'a Pubkey>,
558 update_authority: Option<&'a Pubkey>,
559 mint_authority: Option<&'a Pubkey>,
560 name: Option<String>,
561 symbol: Option<String>,
562 uri: Option<String>,
563}
564
565#[cfg(feature = "token-extensions")]
566impl Default for InitializeMetadataBuilder<'_> {
567 fn default() -> Self {
568 Self::new()
569 }
570}
571
572#[cfg(feature = "token-extensions")]
573impl<'a> InitializeMetadataBuilder<'a> {
574 pub fn new() -> Self {
575 Self {
576 token_program: None,
577 mint: None,
578 owner: None,
579 update_authority: None,
580 mint_authority: None,
581 name: None,
582 symbol: None,
583 uri: None,
584 }
585 }
586
587 pub fn token_program(&mut self, token_program: &'a Pubkey) -> &mut Self {
588 self.token_program = Some(token_program);
589 self
590 }
591
592 pub fn mint(&mut self, mint: &'a Pubkey) -> &mut Self {
593 self.mint = Some(mint);
594 self
595 }
596
597 pub fn owner(&mut self, owner: &'a Pubkey) -> &mut Self {
598 self.owner = Some(owner);
599 self
600 }
601
602 pub fn update_authority(&mut self, update_authority: &'a Pubkey) -> &mut Self {
603 self.update_authority = Some(update_authority);
604 self
605 }
606
607 pub fn mint_authority(&mut self, mint_authority: &'a Pubkey) -> &mut Self {
608 self.mint_authority = Some(mint_authority);
609 self
610 }
611
612 pub fn name(&mut self, name: String) -> &mut Self {
613 self.name = Some(name);
614 self
615 }
616
617 pub fn symbol(&mut self, symbol: String) -> &mut Self {
618 self.symbol = Some(symbol);
619 self
620 }
621
622 pub fn uri(&mut self, uri: String) -> &mut Self {
623 self.uri = Some(uri);
624 self
625 }
626
627 pub fn instruction(&self) -> Instruction {
628 initialize_metadata(
629 self.token_program.unwrap_or(&TOKEN_22_PROGRAM_ID),
630 self.mint.expect("mint_pubkey is not set"),
631 self.update_authority
632 .expect("update_authority_pubkey is not set"),
633 self.mint.expect("mint_pubkey is not set"),
634 self.mint_authority.expect("mint_authority is not set"),
635 self.name.as_ref().expect("name is not set").clone(),
636 self.symbol.as_ref().expect("symbol is not set").clone(),
637 self.uri.as_ref().expect("uri is not set").clone(),
638 )
639 }
640}