saturn_mempool_oracle/
lib.rs

1use account::{create_account, increase_account_size};
2use arch_program::{
3    account::{next_account_info, AccountInfo},
4    msg,
5    program::set_return_data,
6    program_error::ProgramError,
7    pubkey::Pubkey,
8};
9
10mod account;
11mod cpi;
12mod errors;
13mod mempool_entries;
14mod mempool_entry;
15mod ops;
16mod params;
17mod state;
18mod txid;
19
20use params::*;
21use state::*;
22
23pub use {
24    cpi::*,
25    errors::CpiErrorCode,
26    mempool_entries::MempoolEntries,
27    mempool_entry::MempoolEntry,
28    saturn_mempool_oracle_sdk as sdk,
29    ops::{UpdateMempoolEntries, UpdateMempoolEntriesInstruction, UpdateMempoolEntriesOp},
30    txid::Txid,
31};
32
33#[cfg(not(feature = "no-entrypoint"))]
34use arch_program::entrypoint;
35
36/// IMPORTANT! When updating this value, the existing state of the contract
37/// will become invalid. Either reset the contract state or implement a way
38/// to update the state so it's valid when this changes.
39pub const MEMPOOL_ORACLE_ACCOUNTS: usize = 3;
40
41pub use saturn_mempool_oracle_sdk::*;
42
43#[cfg(not(feature = "no-entrypoint"))]
44entrypoint!(process_instruction);
45
46pub fn process_instruction<'a>(
47    program_id: &Pubkey,
48    accounts: &'a [AccountInfo<'static>],
49    instruction_data: &[u8],
50) -> Result<(), ProgramError> {
51    msg!("starting program");
52
53    let instruction = deserialize_instruction(instruction_data)?;
54
55    msg!("Processing mempool oracle instruction");
56
57    let (info_accounts, rem_accounts) = accounts.split_at(MEMPOOL_ORACLE_ACCOUNTS);
58
59    if info_accounts.len() != MEMPOOL_ORACLE_ACCOUNTS {
60        return Err(ProgramError::NotEnoughAccountKeys);
61    }
62
63    msg!("entries ok");
64
65    let return_data: Option<Vec<u8>> = match instruction {
66        UpdateMempoolEntriesInstruction::InitializeProgram => {
67            validate_pda_accounts(*program_id, info_accounts)?;
68
69            if rem_accounts.len() != 3 {
70                return Err(ProgramError::NotEnoughAccountKeys);
71            }
72
73            if *rem_accounts[0].key != OWNER_PUBKEY {
74                msg!(
75                    "invalid signer: {:x} != {:x}",
76                    rem_accounts[0].key,
77                    OWNER_PUBKEY
78                );
79                return Err(ProgramError::IncorrectProgramId);
80            }
81
82            assert!(rem_accounts[0].is_signer, "signer account must be a signer");
83
84            msg!("signer ok");
85
86            for (i, account) in info_accounts.iter().enumerate() {
87                let (pda, bump_seed) = mempool_pda_address(program_id, i as u32);
88
89                if *account.key != pda {
90                    msg!(
91                        "PDA check failed: {:?}",
92                        PdaError::InvalidPda(pda, *account.key)
93                    );
94                    return Err(PdaError::InvalidPda(pda, *account.key))
95                        .map_err(|_| ProgramError::Custom(1))?;
96                }
97
98                if account.lamports() != 0 {
99                    return Err(ProgramError::AccountAlreadyInitialized);
100                }
101
102                // Build signer seeds: [idx_seed, bump]
103                let idx_seed = (i as u32).to_le_bytes();
104                let combined_seeds: [&[u8]; 2] = [&idx_seed, &[bump_seed]];
105                let cpi_signer_seeds: &[&[&[u8]]] = &[&combined_seeds];
106
107                create_account(
108                    account,
109                    &rem_accounts[1],
110                    &rem_accounts[0],
111                    &rem_accounts[2],
112                    cpi_signer_seeds,
113                )?;
114            }
115
116            None
117        }
118        UpdateMempoolEntriesInstruction::ResizeAccounts => {
119            validate_pda_accounts(*program_id, info_accounts)?;
120
121            if rem_accounts.len() != 1 {
122                return Err(ProgramError::NotEnoughAccountKeys);
123            }
124
125            if *rem_accounts[0].key != OWNER_PUBKEY {
126                msg!(
127                    "invalid signer: {:x} != {:x}",
128                    rem_accounts[0].key,
129                    OWNER_PUBKEY
130                );
131                return Err(ProgramError::IncorrectProgramId);
132            }
133
134            assert!(rem_accounts[0].is_signer, "signer account must be a signer");
135
136            msg!("signer ok");
137
138            let size = std::mem::size_of::<MempoolEntries>();
139            for account in info_accounts.iter() {
140                if account.lamports() == 0 {
141                    return Err(ProgramError::UninitializedAccount);
142                }
143
144                increase_account_size(account, size as u32)?;
145            }
146
147            None
148        }
149        UpdateMempoolEntriesInstruction::ModifyEntries(update_mempool_entries_ops) => {
150            validate_pda_accounts(*program_id, info_accounts)?;
151
152            let mut entries = deserialize_multiple_mempool_entries(
153                &mut info_accounts.iter(),
154                MEMPOOL_ORACLE_ACCOUNTS,
155            )?;
156
157            if rem_accounts.len() != 1 {
158                return Err(ProgramError::NotEnoughAccountKeys);
159            }
160
161            if *rem_accounts[0].key != OWNER_PUBKEY {
162                msg!(
163                    "invalid signer: {:x} != {:x}",
164                    rem_accounts[0].key,
165                    OWNER_PUBKEY
166                );
167                return Err(ProgramError::IncorrectProgramId);
168            }
169
170            assert!(rem_accounts[0].is_signer, "signer account must be a signer");
171
172            msg!("signer ok");
173
174            for op in update_mempool_entries_ops {
175                let index = op.txid()[0] as usize % MEMPOOL_ORACLE_ACCOUNTS;
176
177                match op {
178                    UpdateMempoolEntriesOp::AddEntry(entry) => entries[index].add_entry(entry),
179                    UpdateMempoolEntriesOp::DeleteEntry(txid) => entries[index].remove_entry(txid),
180                    UpdateMempoolEntriesOp::UpdateEntry(entry) => {
181                        entries[index].update_entry(entry)
182                    }
183                }
184            }
185
186            None
187        }
188        UpdateMempoolEntriesInstruction::GetEntries(txids) => {
189            validate_pda_accounts(*program_id, info_accounts)?;
190
191            let entries = deserialize_multiple_mempool_entries(
192                &mut info_accounts.iter(),
193                MEMPOOL_ORACLE_ACCOUNTS,
194            )?;
195
196            let computed_entries_bytes = find_mempool_entries(&entries, &txids)?;
197            Some(computed_entries_bytes)
198        }
199    };
200
201    msg!("instruction ok");
202
203    if let Some(return_data) = return_data {
204        set_return_data(&return_data);
205    }
206
207    Ok(())
208}
209
210pub fn id() -> Pubkey {
211    Pubkey([
212        213, 44, 6, 241, 148, 65, 8, 220, 181, 85, 24, 214, 28, 42, 204, 102, 185, 34, 202, 233,
213        24, 149, 13, 242, 149, 96, 68, 183, 219, 90, 109, 163,
214    ])
215}
216
217pub const ID: Pubkey = Pubkey([
218    213, 44, 6, 241, 148, 65, 8, 220, 181, 85, 24, 214, 28, 42, 204, 102, 185, 34, 202, 233, 24,
219    149, 13, 242, 149, 96, 68, 183, 219, 90, 109, 163,
220]);
221
222pub const OWNER_PUBKEY: Pubkey = Pubkey([
223    157, 90, 116, 11, 120, 205, 121, 221, 134, 130, 113, 18, 9, 209, 179, 246, 37, 95, 224, 226,
224    236, 170, 222, 25, 251, 236, 215, 228, 124, 135, 135, 107,
225]);