Skip to main content

antegen_fiber_program/state/
instruction.rs

1use anchor_lang::prelude::*;
2use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
3use std::collections::HashMap;
4
5/// Serializable version of Solana's Instruction for easier handling
6#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)]
7pub struct SerializableInstruction {
8    pub program_id: Pubkey,
9    pub accounts: Vec<SerializableAccountMeta>,
10    pub data: Vec<u8>,
11}
12
13/// Serializable version of AccountMeta
14#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)]
15pub struct SerializableAccountMeta {
16    pub pubkey: Pubkey,
17    pub is_signer: bool,
18    pub is_writable: bool,
19}
20
21impl From<Instruction> for SerializableInstruction {
22    fn from(ix: Instruction) -> Self {
23        SerializableInstruction {
24            program_id: ix.program_id,
25            accounts: ix
26                .accounts
27                .into_iter()
28                .map(|acc| SerializableAccountMeta {
29                    pubkey: acc.pubkey,
30                    is_signer: acc.is_signer,
31                    is_writable: acc.is_writable,
32                })
33                .collect(),
34            data: ix.data,
35        }
36    }
37}
38
39impl From<SerializableInstruction> for Instruction {
40    fn from(ix: SerializableInstruction) -> Self {
41        Instruction {
42            program_id: ix.program_id,
43            accounts: ix
44                .accounts
45                .into_iter()
46                .map(|acc| AccountMeta {
47                    pubkey: acc.pubkey,
48                    is_signer: acc.is_signer,
49                    is_writable: acc.is_writable,
50                })
51                .collect(),
52            data: ix.data,
53        }
54    }
55}
56
57/// Compiled instruction data for space-efficient storage
58#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)]
59pub struct CompiledInstructionData {
60    pub program_id_index: u8,
61    pub accounts: Vec<u8>,
62    pub data: Vec<u8>,
63}
64
65/// Compiled instruction containing deduplicated accounts
66#[derive(AnchorDeserialize, AnchorSerialize, Clone, Debug)]
67pub struct CompiledInstructionV0 {
68    pub num_ro_signers: u8,
69    pub num_rw_signers: u8,
70    pub num_rw: u8,
71    pub instructions: Vec<CompiledInstructionData>,
72    pub accounts: Vec<Pubkey>,
73}
74
75/// Compile an instruction into a space-efficient format
76pub fn compile_instruction(
77    instruction: Instruction,
78) -> Result<CompiledInstructionV0> {
79    let mut pubkeys_to_metadata: HashMap<Pubkey, AccountMeta> = HashMap::new();
80
81    // Add program ID
82    pubkeys_to_metadata.insert(
83        instruction.program_id,
84        AccountMeta {
85            pubkey: instruction.program_id,
86            is_signer: false,
87            is_writable: false,
88        },
89    );
90
91    // Process accounts
92    for acc in &instruction.accounts {
93        let entry = pubkeys_to_metadata
94            .entry(acc.pubkey)
95            .or_insert(AccountMeta {
96                pubkey: acc.pubkey,
97                is_signer: false,
98                is_writable: false,
99            });
100        // Don't merge permissions from sentinel accounts (Anchor uses program_id
101        // as pubkey for None optional accounts). Merging would pollute the
102        // program_id entry's permissions and misclassify its sort bucket.
103        if acc.pubkey != instruction.program_id {
104            entry.is_signer |= acc.is_signer;
105            entry.is_writable |= acc.is_writable;
106        }
107    }
108
109    // Sort accounts by priority
110    let mut sorted_accounts: Vec<Pubkey> = pubkeys_to_metadata.keys().cloned().collect();
111    sorted_accounts.sort_by(|a, b| {
112        let a_meta = &pubkeys_to_metadata[a];
113        let b_meta = &pubkeys_to_metadata[b];
114
115        fn get_priority(meta: &AccountMeta) -> u8 {
116            match (meta.is_signer, meta.is_writable) {
117                (true, true) => 0,
118                (true, false) => 1,
119                (false, true) => 2,
120                (false, false) => 3,
121            }
122        }
123
124        get_priority(a_meta).cmp(&get_priority(b_meta))
125    });
126
127    // Count account types
128    let mut num_rw_signers = 0u8;
129    let mut num_ro_signers = 0u8;
130    let mut num_rw = 0u8;
131
132    for pubkey in &sorted_accounts {
133        let meta = &pubkeys_to_metadata[pubkey];
134        if meta.is_signer && meta.is_writable {
135            num_rw_signers += 1;
136        } else if meta.is_signer && !meta.is_writable {
137            num_ro_signers += 1;
138        } else if meta.is_writable {
139            num_rw += 1;
140        }
141    }
142
143    // Create index mapping
144    let accounts_to_index: HashMap<Pubkey, u8> = sorted_accounts
145        .iter()
146        .enumerate()
147        .map(|(i, k)| (*k, i as u8))
148        .collect();
149
150    // Create compiled instruction
151    let compiled_instruction = CompiledInstructionData {
152        program_id_index: *accounts_to_index.get(&instruction.program_id).unwrap(),
153        accounts: instruction
154            .accounts
155            .iter()
156            .map(|acc| *accounts_to_index.get(&acc.pubkey).unwrap())
157            .collect(),
158        data: instruction.data,
159    };
160
161    Ok(CompiledInstructionV0 {
162        num_ro_signers,
163        num_rw_signers,
164        num_rw,
165        instructions: vec![compiled_instruction],
166        accounts: sorted_accounts,
167    })
168}
169
170/// Decompile a compiled instruction back to a regular instruction
171pub fn decompile_instruction(compiled: &CompiledInstructionV0) -> Result<Instruction> {
172    if compiled.instructions.is_empty() {
173        return Err(ProgramError::InvalidInstructionData.into());
174    }
175
176    let ix = &compiled.instructions[0];
177    let program_id = compiled.accounts[ix.program_id_index as usize];
178
179    let accounts: Vec<AccountMeta> = ix
180        .accounts
181        .iter()
182        .enumerate()
183        .map(|(_i, &idx)| {
184            let pubkey = compiled.accounts[idx as usize];
185            let is_writable = if idx < compiled.num_rw_signers {
186                true
187            } else if idx < compiled.num_rw_signers + compiled.num_ro_signers {
188                false
189            } else if idx < compiled.num_rw_signers + compiled.num_ro_signers + compiled.num_rw {
190                true
191            } else {
192                false
193            };
194            let is_signer = idx < compiled.num_rw_signers + compiled.num_ro_signers;
195
196            AccountMeta {
197                pubkey,
198                is_signer,
199                is_writable,
200            }
201        })
202        .collect();
203
204    Ok(Instruction {
205        program_id,
206        accounts,
207        data: ix.data.clone(),
208    })
209}