yellowstone_shield_client/
lib.rs

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/// Instruction builder for creating a solana account.
170///
171/// ### Accounts:
172///
173///   0. `[signer]` payer
174///   1. `[writable]` mint
175///   2. `[optional]` system_program (default to `11111111111111111111111111111111`)
176#[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    /// The account paying for the storage fees
191    #[inline(always)]
192    pub fn payer(&mut self, payer: &'a Pubkey) -> &mut Self {
193        self.payer = Some(payer);
194        self
195    }
196
197    /// The account to be created
198    #[inline(always)]
199    pub fn account(&mut self, account: &'a Pubkey) -> &mut Self {
200        self.account = Some(account);
201        self
202    }
203
204    /// The space required for the account
205    #[inline(always)]
206    pub fn space(&mut self, space: usize) -> &mut Self {
207        self.space = Some(space);
208        self
209    }
210
211    /// The program to own the account
212    #[inline(always)]
213    pub fn owner(&mut self, owner: &'a Pubkey) -> &mut Self {
214        self.owner = Some(owner);
215        self
216    }
217
218    /// The rent to be paid for the account
219    #[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    /// The mint account to be initialized
259    #[inline(always)]
260    pub fn mint(&mut self, mint: &'a Pubkey) -> &mut Self {
261        self.mint = Some(mint);
262        self
263    }
264
265    /// The authority that can mint new tokens
266    #[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    /// The authority that can freeze token accounts
273    #[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    /// The token program
280    #[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    /// The token program
318    #[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    /// The mint account
325    #[inline(always)]
326    pub fn mint(&mut self, mint: &'a Pubkey) -> &mut Self {
327        self.mint = Some(mint);
328        self
329    }
330
331    /// The metadata account
332    #[inline(always)]
333    pub fn metadata(&mut self, metadata: Pubkey) -> &mut Self {
334        self.metadata = Some(metadata);
335        self
336    }
337
338    /// The metadata account
339    #[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    /// The token program
382    #[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    /// The mint account
389    #[inline(always)]
390    pub fn mint(&mut self, mint: &'a Pubkey) -> &mut Self {
391        self.mint = Some(mint);
392        self
393    }
394
395    /// The owner of the account
396    #[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    /// The token program
436    #[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    /// The mint account
443    #[inline(always)]
444    pub fn mint(&mut self, mint: &'a Pubkey) -> &mut Self {
445        self.mint = Some(mint);
446        self
447    }
448
449    /// The account to mint to
450    #[inline(always)]
451    pub fn account(&mut self, account: &'a Pubkey) -> &mut Self {
452        self.account = Some(account);
453        self
454    }
455
456    /// The owner of the account
457    #[inline(always)]
458    pub fn owner(&mut self, owner: &'a Pubkey) -> &mut Self {
459        self.owner = Some(owner);
460        self
461    }
462
463    /// Add a signer
464    #[inline(always)]
465    pub fn add_signer(&mut self, signer: &'a Pubkey) -> &mut Self {
466        self.signers.push(signer);
467        self
468    }
469
470    /// The amount to mint
471    #[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    /// Set the entire list of instructions for the transaction
508    #[inline(always)]
509    pub fn instructions(&mut self, instructions: Vec<Instruction>) -> &mut Self {
510        self.instructions = instructions;
511        self
512    }
513
514    /// Add an instruction to the transaction
515    #[inline(always)]
516    pub fn instruction(&mut self, instruction: Instruction) -> &mut Self {
517        self.instructions.push(instruction);
518        self
519    }
520
521    /// Add a signer to the transaction
522    #[inline(always)]
523    pub fn signer(&mut self, signer: &'a Keypair) -> &mut Self {
524        self.signers.push(signer);
525        self
526    }
527
528    /// Set the payer for the transaction
529    #[inline(always)]
530    pub fn payer(&mut self, payer: &'a Pubkey) -> &mut Self {
531        self.payer = Some(payer);
532        self
533    }
534
535    /// Set the recent blockhash for the transaction
536    #[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    /// Build the transaction
543    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}