yellowstone_shield_parser/
instructions.rs

1use borsh::BorshDeserialize;
2use solana_program::pubkey::Pubkey;
3use yellowstone_shield_client::instructions::{
4    AddIdentity as AddIdentityIxAccounts, AddIdentityInstructionArgs as AddIdentityIxData,
5    CreatePolicy as CreatePolicyIxAccounts, CreatePolicyInstructionArgs as CreatePolicyIxData,
6    RemoveIdentity as RemoveIdentityIxAccounts,
7    RemoveIdentityInstructionArgs as RemoveIdentityIxData,
8};
9
10use yellowstone_shield_client::ID;
11
12/// Shield Instructions
13#[derive(Debug)]
14#[allow(dead_code)]
15pub enum ShieldProgramIx {
16    CreatePolicy(CreatePolicyIxAccounts, CreatePolicyIxData),
17    AddIdentity(AddIdentityIxAccounts, AddIdentityIxData),
18    RemoveIdentity(RemoveIdentityIxAccounts, RemoveIdentityIxData),
19}
20
21pub fn parse_instruction(
22    program_id: &Pubkey,
23    accounts: &[Pubkey],
24    data: &[u8],
25) -> Result<ShieldProgramIx, String> {
26    if program_id != &ID {
27        return Err(format!(
28            "Invalid program ID: expected {}, got {}",
29            ID, program_id
30        ));
31    }
32
33    if data.is_empty() {
34        return Err("Instruction data is empty".to_owned());
35    }
36
37    let accounts_len = accounts.len();
38    let ix_discriminator = data[0];
39    let mut ix_data = &data[1..];
40
41    match ix_discriminator {
42        0 => {
43            check_min_accounts_req(accounts_len, 6)?;
44            let ix_accounts = CreatePolicyIxAccounts {
45                mint: accounts[0],
46                token_account: accounts[1],
47                policy: accounts[2],
48                payer: accounts[3],
49                owner: accounts[4],
50                system_program: accounts[5],
51            };
52            let de_ix_data: CreatePolicyIxData =
53                BorshDeserialize::deserialize(&mut ix_data).map_err(|e| e.to_string())?;
54            Ok(ShieldProgramIx::CreatePolicy(ix_accounts, de_ix_data))
55        }
56        1 => {
57            check_min_accounts_req(accounts_len, 6)?;
58            let ix_accounts = AddIdentityIxAccounts {
59                mint: accounts[0],
60                token_account: accounts[1],
61                policy: accounts[2],
62                payer: accounts[3],
63                owner: accounts[4],
64                system_program: accounts[5],
65            };
66            let de_ix_data: AddIdentityIxData =
67                BorshDeserialize::deserialize(&mut ix_data).map_err(|e| e.to_string())?;
68            Ok(ShieldProgramIx::AddIdentity(ix_accounts, de_ix_data))
69        }
70        2 => {
71            check_min_accounts_req(accounts_len, 4)?;
72            let ix_accounts = RemoveIdentityIxAccounts {
73                mint: accounts[0],
74                token_account: accounts[1],
75                policy: accounts[2],
76                owner: accounts[3],
77            };
78            let de_ix_data: RemoveIdentityIxData =
79                BorshDeserialize::deserialize(&mut ix_data).map_err(|e| e.to_string())?;
80            Ok(ShieldProgramIx::RemoveIdentity(ix_accounts, de_ix_data))
81        }
82        _ => Err("Invalid Instruction discriminator".to_owned()),
83    }
84}
85
86fn check_min_accounts_req(actual: usize, expected: usize) -> Result<(), String> {
87    if actual < expected {
88        Err(format!(
89            "Too few accounts provided: expected {expected}, got {actual}"
90        ))
91    } else {
92        Ok(())
93    }
94}