oil_api/
sdk.rs

1use solana_program::pubkey::Pubkey;
2use spl_associated_token_account::get_associated_token_address;
3use steel::*;
4
5use crate::{
6    consts::{AUCTION, BOARD, MINT_ADDRESS, SOL_MINT, TREASURY_ADDRESS},
7    instruction::{self, *},
8    state::*,
9};
10
11pub fn log(signer: Pubkey, msg: &[u8]) -> Instruction {
12    let mut data = Log {}.to_bytes();
13    data.extend_from_slice(msg);
14    Instruction {
15        program_id: crate::ID,
16        accounts: vec![AccountMeta::new(signer, true)],
17        data: data,
18    }
19}
20
21pub fn program_log(accounts: &[AccountInfo], msg: &[u8]) -> Result<(), ProgramError> {
22    // Derive Board PDA to use as signer for log instruction
23    let (board_address, _) = board_pda();
24    invoke_signed(&log(board_address, msg), accounts, &crate::ID, &[BOARD])
25}
26
27/// Log event for auction-based instructions (uses Auction PDA instead of Board)
28pub fn auction_program_log(accounts: &[AccountInfo], msg: &[u8]) -> Result<(), ProgramError> {
29    // Derive Auction PDA to use as signer for log instruction
30    let (auction_address, _) = auction_pda();
31    invoke_signed(&log(auction_address, msg), accounts, &crate::ID, &[AUCTION])
32}
33
34// let [signer_info, board_info, config_info, mint_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program] = accounts else {
35
36// pub fn initialize(
37//     signer: Pubkey,
38//     barrel_authority: Pubkey,
39//     fee_collector: Pubkey,
40//     swap_program: Pubkey,
41//     var_address: Pubkey,
42//     admin_fee: u64,
43// ) -> Instruction {
44//     let board_address = board_pda().0;
45//     let config_address = config_pda().0;
46//     let mint_address = MINT_ADDRESS;
47//     let treasury_address = TREASURY_ADDRESS;
48//     let treasury_tokens_address = treasury_tokens_address();
49//     Instruction {
50//         program_id: crate::ID,
51//         accounts: vec![
52//             AccountMeta::new(signer, true),
53//             AccountMeta::new(board_address, false),
54//             AccountMeta::new(config_address, false),
55//             AccountMeta::new(mint_address, false),
56//             AccountMeta::new(treasury_address, false),
57//             AccountMeta::new(treasury_tokens_address, false),
58//             AccountMeta::new_readonly(system_program::ID, false),
59//             AccountMeta::new_readonly(spl_token::ID, false),
60//             AccountMeta::new_readonly(spl_associated_token_account::ID, false),
61//         ],
62//         data: Initialize {
63//             barrel_authority: barrel_authority.to_bytes(),
64//             fee_collector: fee_collector.to_bytes(),
65//             swap_program: swap_program.to_bytes(),
66//             var_address: var_address.to_bytes(),
67//             admin_fee: admin_fee.to_le_bytes(),
68//         }
69//         .to_bytes(),
70//     }
71// }
72
73// let [signer_info, automation_info, executor_info, miner_info, system_program] = accounts else {
74
75/// Set up automation for a miner. If the miner doesn't exist yet, pass a referrer to set it.
76/// If a referrer is provided and the miner is new, the referral account must be included.
77pub fn automate(
78    signer: Pubkey,
79    authority: Pubkey,
80    amount: u64,
81    deposit: u64,
82    executor: Pubkey,
83    fee: u64,
84    mask: u64,
85    strategy: u8,
86    reload: bool,
87    referrer: Option<Pubkey>,
88    pooled: bool,
89    is_new_miner: bool,
90    access_code_hash: Option<[u8; 32]>,
91) -> Instruction {
92    let automation_address = automation_pda(authority).0;
93    let miner_address = miner_pda(authority).0;
94    let config_address = config_pda().0;
95    let referrer_pk = referrer.unwrap_or(Pubkey::default());
96    
97    let mut accounts = vec![
98            AccountMeta::new(signer, true), // 0: signer (payer)
99            AccountMeta::new(authority, false), // 1: authority (user's wallet)
100            AccountMeta::new(automation_address, false), // 2: automation
101            AccountMeta::new(executor, false), // 3: executor
102            AccountMeta::new(miner_address, false), // 4: miner
103            AccountMeta::new_readonly(system_program::ID, false), // 5: system_program
104            AccountMeta::new_readonly(crate::ID, false), // 6: oil_program
105            AccountMeta::new_readonly(config_address, false), // 7: config
106    ];
107    
108    // Token accounts (user_wrapped_sol, automation_wrapped_sol, token_program, program_signer (optional), payer (optional), mint, ata_program)
109    // These are added by the client, not here in the SDK
110    
111    // Add referral account if referrer is provided and miner is new (for incrementing total_referred)
112    if is_new_miner && referrer.is_some() && referrer_pk != Pubkey::default() {
113        let referral_address = referral_pda(referrer_pk).0;
114        accounts.push(AccountMeta::new(referral_address, false));
115    }
116    
117    // Add Code account if access_code_hash is provided
118    let access_code_hash_bytes = access_code_hash.unwrap_or([0; 32]);
119    if access_code_hash_bytes != [0; 32] {
120        let (code_address, _) = Code::pda(access_code_hash_bytes, authority);
121        accounts.push(AccountMeta::new(code_address, false)); // code (optional)
122    }
123    
124    Instruction {
125        program_id: crate::ID,
126        accounts,
127        data: Automate {
128            amount: amount.to_le_bytes(),
129            deposit: deposit.to_le_bytes(),
130            fee: fee.to_le_bytes(),
131            mask: mask.to_le_bytes(),
132            strategy: strategy as u8,
133            reload: (reload as u64).to_le_bytes(),
134            referrer: referrer_pk.to_bytes(),
135            pooled: pooled as u8,
136            access_code_hash: access_code_hash_bytes,
137        }
138        .to_bytes(),
139    }
140}
141
142/// Claim SOL rewards with single-tier referral system.
143/// 
144/// If the miner has a referrer, 1.0% of the claim goes to the referrer.
145/// 
146/// Account structure:
147/// - Base: signer, miner, system_program
148/// - If miner has referrer (required): [miner_referrer, referral_referrer]
149pub fn claim_sol(
150    signer: Pubkey,
151    referrer_miner: Option<Pubkey>, // Referrer's miner PDA (if miner has referrer)
152    referrer_referral: Option<Pubkey>, // Referrer's referral PDA (if miner has referrer)
153) -> Instruction {
154    let miner_address = miner_pda(signer).0;
155    
156    let mut accounts = vec![
157        AccountMeta::new(signer, true),
158        AccountMeta::new(miner_address, false),
159        AccountMeta::new_readonly(system_program::ID, false),
160    ];
161    
162    // Add referrer accounts if provided (required if miner has referrer)
163    if let (Some(miner_pubkey), Some(referral_pubkey)) = (referrer_miner, referrer_referral) {
164        accounts.push(AccountMeta::new(miner_pubkey, false));
165        accounts.push(AccountMeta::new(referral_pubkey, false));
166    }
167    
168    Instruction {
169        program_id: crate::ID,
170        accounts,
171        data: ClaimSOL {}.to_bytes(),
172    }
173}
174
175// let [signer_info, miner_info, mint_info, recipient_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program] =
176
177/// Claim OIL rewards with single-tier referral system.
178/// 
179/// If the miner has a referrer, 1.0% of the claim goes to the referrer.
180/// 
181/// Account structure:
182/// - Base: signer, miner, mint, recipient, treasury, treasury_tokens, system_program, token_program, associated_token_program
183/// - If miner has referrer (required): [miner_referrer, referral_referrer, referral_referrer_oil_ata]
184pub fn claim_oil(
185    signer: Pubkey,
186    referrer_miner: Option<Pubkey>, // Referrer's miner PDA (if miner has referrer)
187    referrer_referral: Option<Pubkey>, // Referrer's referral PDA (if miner has referrer)
188    referrer_referral_oil_ata: Option<Pubkey>, // Referrer's referral OIL ATA (if miner has referrer)
189) -> Instruction {
190    let miner_address = miner_pda(signer).0;
191    let treasury_address = treasury_pda().0;
192    let treasury_tokens_address = get_associated_token_address(&treasury_address, &MINT_ADDRESS);
193    let recipient_address = get_associated_token_address(&signer, &MINT_ADDRESS);
194    
195    let mut accounts = vec![
196        AccountMeta::new(signer, true),
197        AccountMeta::new(miner_address, false),
198        AccountMeta::new(MINT_ADDRESS, false),
199        AccountMeta::new(recipient_address, false),
200        AccountMeta::new(treasury_address, false),
201        AccountMeta::new(treasury_tokens_address, false),
202        AccountMeta::new_readonly(system_program::ID, false),
203        AccountMeta::new_readonly(spl_token::ID, false),
204        AccountMeta::new_readonly(spl_associated_token_account::ID, false),
205    ];
206    
207    // Add referrer accounts if provided (required if miner has referrer)
208    if let (Some(miner_pubkey), Some(referral_pubkey), Some(oil_ata_pubkey)) = 
209        (referrer_miner, referrer_referral, referrer_referral_oil_ata) {
210        accounts.push(AccountMeta::new(miner_pubkey, false));
211        accounts.push(AccountMeta::new(referral_pubkey, false));
212        accounts.push(AccountMeta::new(oil_ata_pubkey, false));
213    }
214    
215    Instruction {
216        program_id: crate::ID,
217        accounts,
218        data: ClaimOIL {}.to_bytes(),
219    }
220}
221
222
223pub fn close(signer: Pubkey, round_id: u64, rent_payer: Pubkey) -> Instruction {
224    let board_address = board_pda().0;
225    let round_address = round_pda(round_id).0;
226    let treasury_address = TREASURY_ADDRESS;
227    Instruction {
228        program_id: crate::ID,
229        accounts: vec![
230            AccountMeta::new(signer, true),
231            AccountMeta::new(board_address, false),
232            AccountMeta::new(rent_payer, false),
233            AccountMeta::new(round_address, false),
234            AccountMeta::new(treasury_address, false),
235            AccountMeta::new_readonly(system_program::ID, false),
236        ],
237        data: Close {}.to_bytes(),
238    }
239}
240
241/// Deploy SOL to prospect on squares. Pass a referrer pubkey for new miners to set up referral.
242/// Set `pooled` to true to join the mining pool (rewards shared proportionally).
243pub fn deploy(
244    signer: Pubkey,
245    authority: Pubkey,
246    amount: u64,
247    round_id: u64,
248    squares: [bool; 25],
249    referrer: Option<Pubkey>,
250    pooled: bool,
251    access_code_hash: Option<[u8; 32]>,
252) -> Instruction {
253    let automation_address = automation_pda(authority).0;
254    let board_address = board_pda().0;
255    let config_address = config_pda().0;
256    let miner_address = miner_pda(authority).0;
257    let round_address = round_pda(round_id).0;
258    let entropy_var_address = entropy_rng_api::state::var_pda(board_address, 0).0;
259
260    // Convert array of 25 booleans into a 32-bit mask where each bit represents whether
261    // that square index is selected (1) or not (0)
262    let mut mask: u32 = 0;
263    for (i, &square) in squares.iter().enumerate() {
264        if square {
265            mask |= 1 << i;
266        }
267    }
268    
269    // Get referrer bytes (default to zero pubkey if no referrer).
270    let referrer_pubkey = referrer.unwrap_or(Pubkey::default());
271    let referrer_bytes = referrer_pubkey.to_bytes();
272
273    // Derive wrapped SOL ATAs
274    let user_wrapped_sol_ata = get_associated_token_address(&authority, &SOL_MINT);
275    let round_wrapped_sol_ata = get_associated_token_address(&round_address, &SOL_MINT);
276
277    // Build accounts list - must match program structure:
278    // Oil accounts: base (9) + token (5) + optional referral (1) = 14-15
279    // Entropy accounts: var + program = 2 (always exactly 2)
280    let mut accounts = vec![
281        // Base accounts (9)
282        AccountMeta::new(signer, true), // 0: signer
283        AccountMeta::new(authority, false), // 1: authority
284        AccountMeta::new(automation_address, false), // 2: automation
285        AccountMeta::new(board_address, false), // 3: board
286        AccountMeta::new_readonly(config_address, false), // 4: config
287        AccountMeta::new(miner_address, false), // 5: miner
288        AccountMeta::new(round_address, false), // 6: round
289        AccountMeta::new_readonly(system_program::ID, false), // 7: system_program
290        AccountMeta::new_readonly(crate::ID, false), // 8: oil_program
291        // Token accounts (5)
292        AccountMeta::new(user_wrapped_sol_ata, false), // 9: user_wrapped_sol
293        AccountMeta::new(round_wrapped_sol_ata, false), // 10: round_wrapped_sol
294        AccountMeta::new_readonly(spl_token::ID, false), // 11: token_program
295        AccountMeta::new_readonly(SOL_MINT, false), // 12: mint (SOL_MINT)
296        AccountMeta::new_readonly(spl_associated_token_account::ID, false), // 13: associated_token_program
297    ];
298    
299    // Add referral account if referrer is provided (in oil_accounts, before code account)
300    if referrer_pubkey != Pubkey::default() {
301        let referral_address = referral_pda(referrer_pubkey).0;
302        accounts.push(AccountMeta::new(referral_address, false)); // referral (optional, in oil_accounts)
303    }
304    
305    // Add Code account if access_code_hash is provided (in oil_accounts, after referral if present)
306    let access_code_hash_bytes = access_code_hash.unwrap_or([0; 32]);
307    if access_code_hash_bytes != [0; 32] {
308        let (code_address, _) = Code::pda(access_code_hash_bytes, authority);
309        accounts.push(AccountMeta::new(code_address, false)); // code (optional, in oil_accounts)
310    }
311    
312    // Entropy accounts (always exactly 2, come after all oil_accounts)
313    accounts.push(AccountMeta::new(entropy_var_address, false)); // entropy_var
314    accounts.push(AccountMeta::new_readonly(entropy_rng_api::ID, false)); // entropy_program
315
316    Instruction {
317        program_id: crate::ID,
318        accounts,
319        data: Deploy {
320            amount: amount.to_le_bytes(),
321            squares: mask.to_le_bytes(),
322            referrer: referrer_bytes,
323            pooled: if pooled { 1 } else { 0 },
324            access_code_hash: access_code_hash_bytes,
325        }
326        .to_bytes(),
327    }
328}
329
330/// Deploy instruction for automation (omits wrapped token accounts since automation uses native SOL)
331/// Account structure: base (9) + token programs only (3) + optional referral (1) + optional code (1) + entropy (2) = 12-16 accounts
332pub fn deploy_auto(
333    signer: Pubkey,
334    authority: Pubkey,
335    amount: u64,
336    round_id: u64,
337    squares: [bool; 25],
338    referrer: Option<Pubkey>,
339    pooled: bool,
340    access_code_hash: Option<[u8; 32]>,
341) -> Instruction {
342    let automation_address = automation_pda(authority).0;
343    let board_address = board_pda().0;
344    let config_address = config_pda().0;
345    let miner_address = miner_pda(authority).0;
346    let round_address = round_pda(round_id).0;
347    let entropy_var_address = entropy_rng_api::state::var_pda(board_address, 0).0;
348
349    // Convert array of 25 booleans into a 32-bit mask where each bit represents whether
350    // that square index is selected (1) or not (0)
351    let mut mask: u32 = 0;
352    for (i, &square) in squares.iter().enumerate() {
353        if square {
354            mask |= 1 << i;
355        }
356    }
357    
358    // Get referrer bytes (default to zero pubkey if no referrer).
359    let referrer_pubkey = referrer.unwrap_or(Pubkey::default());
360    let referrer_bytes = referrer_pubkey.to_bytes();
361
362    // Build accounts list - for automation, omit wrapped token ATAs:
363    // Base (9) + token programs only (3) + optional referral (1) = 12-13
364    // Entropy accounts: var + program = 2 (always exactly 2)
365    // Note: Program allows 12 accounts even when signer != authority (automation pattern)
366    let mut accounts = vec![
367        // Base accounts (9)
368        AccountMeta::new(signer, true), // 0: signer
369        AccountMeta::new(authority, false), // 1: authority
370        AccountMeta::new(automation_address, false), // 2: automation
371        AccountMeta::new(board_address, false), // 3: board
372        AccountMeta::new_readonly(config_address, false), // 4: config
373        AccountMeta::new(miner_address, false), // 5: miner
374        AccountMeta::new(round_address, false), // 6: round
375        AccountMeta::new_readonly(system_program::ID, false), // 7: system_program
376        AccountMeta::new_readonly(crate::ID, false), // 8: oil_program
377        // Token programs only (3) - no ATAs for automation
378        AccountMeta::new_readonly(spl_token::ID, false), // 9: token_program
379        AccountMeta::new_readonly(SOL_MINT, false), // 10: mint (SOL_MINT)
380        AccountMeta::new_readonly(spl_associated_token_account::ID, false), // 11: associated_token_program
381    ];
382    
383    // Add referral account if referrer is provided (in oil_accounts, before code account)
384    if referrer_pubkey != Pubkey::default() {
385        let referral_address = referral_pda(referrer_pubkey).0;
386        accounts.push(AccountMeta::new(referral_address, false)); // referral (optional, in oil_accounts)
387    }
388    
389    // Add Code account if access_code_hash is provided (in oil_accounts, after referral if present)
390    let access_code_hash_bytes = access_code_hash.unwrap_or([0; 32]);
391    if access_code_hash_bytes != [0; 32] {
392        let (code_address, _) = Code::pda(access_code_hash_bytes, authority);
393        accounts.push(AccountMeta::new(code_address, false)); // code (optional, in oil_accounts)
394    }
395    
396    // Entropy accounts (always exactly 2, come after all oil_accounts)
397    accounts.push(AccountMeta::new(entropy_var_address, false)); // entropy_var
398    accounts.push(AccountMeta::new_readonly(entropy_rng_api::ID, false)); // entropy_program
399
400    Instruction {
401        program_id: crate::ID,
402        accounts,
403        data: Deploy {
404            amount: amount.to_le_bytes(),
405            squares: mask.to_le_bytes(),
406            referrer: referrer_bytes,
407            pooled: if pooled { 1 } else { 0 },
408            access_code_hash: access_code_hash_bytes,
409        }
410        .to_bytes(),
411    }
412}
413
414// let [pool, user_source_token, user_destination_token, a_vault, b_vault, a_token_vault, b_token_vault, a_vault_lp_mint, b_vault_lp_mint, a_vault_lp, b_vault_lp, protocol_token_fee, user_key, vault_program, token_program] =
415
416pub fn buyback(signer: Pubkey, swap_accounts: &[AccountMeta], swap_data: &[u8]) -> Instruction {
417    let board_address = board_pda().0;
418    let config_address = config_pda().0;
419    let mint_address = MINT_ADDRESS;
420    let treasury_address = TREASURY_ADDRESS;
421    let treasury_oil_address = get_associated_token_address(&treasury_address, &MINT_ADDRESS);
422    let treasury_sol_address = get_associated_token_address(&treasury_address, &SOL_MINT);
423    let mut accounts = vec![
424        AccountMeta::new(signer, true),
425        AccountMeta::new(board_address, false),
426        AccountMeta::new_readonly(config_address, false),
427        AccountMeta::new(mint_address, false),
428        AccountMeta::new(treasury_address, false),
429        AccountMeta::new(treasury_oil_address, false),
430        AccountMeta::new(treasury_sol_address, false),
431        AccountMeta::new_readonly(spl_token::ID, false),
432        AccountMeta::new_readonly(crate::ID, false),
433    ];
434    for account in swap_accounts.iter() {
435        let mut acc_clone = account.clone();
436        acc_clone.is_signer = false;
437        accounts.push(acc_clone);
438    }
439    let mut data = Buyback {}.to_bytes();
440    data.extend_from_slice(swap_data);
441    Instruction {
442        program_id: crate::ID,
443        accounts,
444        data,
445    }
446}
447
448// let [signer_info, board_info, config_info, fee_collector_info, mint_info, round_info, round_next_info, top_miner_info, treasury_info, treasury_tokens_info, system_program, token_program, oil_program, slot_hashes_sysvar] =
449
450pub fn reset(
451    signer: Pubkey,
452    fee_collector: Pubkey,
453    round_id: u64,
454    top_miner: Pubkey,
455    var_address: Pubkey,
456) -> Instruction {
457    reset_with_miners(signer, fee_collector, round_id, top_miner, var_address, &[])
458}
459
460pub fn reset_with_miners(
461    signer: Pubkey,
462    fee_collector: Pubkey,
463    round_id: u64,
464    top_miner: Pubkey,
465    var_address: Pubkey,
466    miner_accounts: &[Pubkey],
467) -> Instruction {
468    let board_address = board_pda().0;
469    let config_address = config_pda().0;
470    let mint_address = MINT_ADDRESS;
471    let round_address = round_pda(round_id).0;
472    let round_next_address = round_pda(round_id + 1).0;
473    let top_miner_address = miner_pda(top_miner).0;
474    let treasury_address = TREASURY_ADDRESS;
475    let treasury_tokens_address = treasury_tokens_address();
476    let pool_address = pool_pda().0;
477    let mint_authority_address = oil_mint_api::state::authority_pda().0;
478    let mut reset_instruction = Instruction {
479        program_id: crate::ID,
480        accounts: vec![
481            AccountMeta::new(signer, true),
482            AccountMeta::new(board_address, false),
483            AccountMeta::new(config_address, false),
484            AccountMeta::new(fee_collector, false),
485            AccountMeta::new(mint_address, false),
486            AccountMeta::new(round_address, false),
487            AccountMeta::new(round_next_address, false),
488            AccountMeta::new(top_miner_address, false),
489            AccountMeta::new(treasury_address, false),
490            AccountMeta::new(pool_address, false),
491            AccountMeta::new(treasury_tokens_address, false),
492            AccountMeta::new_readonly(system_program::ID, false),
493            AccountMeta::new_readonly(spl_token::ID, false),
494            AccountMeta::new_readonly(crate::ID, false),
495            AccountMeta::new_readonly(sysvar::slot_hashes::ID, false),
496            AccountMeta::new_readonly(SOL_MINT, false),
497            // Entropy accounts (these are in "other_accounts" after the split)
498            AccountMeta::new(var_address, false),
499            AccountMeta::new_readonly(entropy_rng_api::ID, false),
500            // Mint accounts.
501            AccountMeta::new(mint_authority_address, false),
502            AccountMeta::new_readonly(oil_mint_api::ID, false),
503        ],
504        data: Reset {}.to_bytes(),
505    };
506    
507    // Add miner accounts for seeker rewards (optional)
508    for miner_pubkey in miner_accounts {
509        reset_instruction.accounts.push(AccountMeta::new(
510            miner_pda(*miner_pubkey).0,
511            false,
512        ));
513    }
514    
515    reset_instruction
516}
517    
518// let [signer_info, automation_info, board_info, miner_info, round_info, treasury_info, system_program] =
519
520pub fn checkpoint(signer: Pubkey, authority: Pubkey, round_id: u64) -> Instruction {
521    let miner_address = miner_pda(authority).0;
522    let board_address = board_pda().0;
523    let round_address = round_pda(round_id).0;
524    let treasury_address = TREASURY_ADDRESS;
525    Instruction {
526        program_id: crate::ID,
527        accounts: vec![
528            AccountMeta::new(signer, true), // payer (session payer or regular wallet, receives bot fee)
529            AccountMeta::new(authority, false), // authority (user's wallet, for PDA derivation - must be writable when combined with deploy)
530            AccountMeta::new(board_address, false),
531            AccountMeta::new(miner_address, false),
532            AccountMeta::new(round_address, false),
533            AccountMeta::new(treasury_address, false),
534            AccountMeta::new_readonly(system_program::ID, false),
535        ],
536        data: Checkpoint {}.to_bytes(),
537    }
538}
539
540pub fn set_admin(signer: Pubkey, admin: Pubkey) -> Instruction {
541    let config_address = config_pda().0;
542    Instruction {
543        program_id: crate::ID,
544        accounts: vec![
545            AccountMeta::new(signer, true),
546            AccountMeta::new(config_address, false),
547            AccountMeta::new_readonly(system_program::ID, false),
548        ],
549        data: SetAdmin {
550            admin: admin.to_bytes(),
551        }
552        .to_bytes(),
553    }
554}
555
556pub fn set_admin_fee(signer: Pubkey, admin_fee: u64) -> Instruction {
557    let config_address = config_pda().0;
558    Instruction {
559        program_id: crate::ID,
560        accounts: vec![
561            AccountMeta::new(signer, true),
562            AccountMeta::new(config_address, false),
563            AccountMeta::new_readonly(system_program::ID, false),
564        ],
565        data: SetAdminFee {
566            admin_fee: admin_fee.to_le_bytes(),
567        }
568        .to_bytes(),
569    }
570}
571
572pub fn set_fee_collector(signer: Pubkey, fee_collector: Pubkey) -> Instruction {
573    let config_address = config_pda().0;
574    Instruction {
575        program_id: crate::ID,
576        accounts: vec![
577            AccountMeta::new(signer, true),
578            AccountMeta::new(config_address, false),
579            AccountMeta::new_readonly(system_program::ID, false),
580        ],
581        data: SetFeeCollector {
582            fee_collector: fee_collector.to_bytes(),
583        }
584        .to_bytes(),
585    }
586}
587
588/// Sets the TGE (Token Generation Event) timestamp.
589/// If current time < tge_timestamp, pre-mine is active.
590/// Set to 0 to disable pre-mine.
591/// Admin-only instruction.
592pub fn set_tge_timestamp(signer: Pubkey, tge_timestamp: i64) -> Instruction {
593    let config_address = config_pda().0;
594    Instruction {
595        program_id: crate::ID,
596        accounts: vec![
597            AccountMeta::new(signer, true),
598            AccountMeta::new(config_address, false),
599            AccountMeta::new_readonly(system_program::ID, false),
600        ],
601        data: SetTgeTimestamp {
602            tge_timestamp: tge_timestamp.to_le_bytes(),
603        }
604        .to_bytes(),
605    }
606}
607
608pub fn set_auction(
609    signer: Pubkey,
610    halving_period_seconds: u64,
611    last_halving_time: u64,
612    base_mining_rates: [u64; 4],
613    auction_duration_seconds: u64,
614    starting_prices: [u64; 4],
615    _well_id: u64, // Kept for backwards compatibility, but not used (always updates auction only)
616) -> Instruction {
617    let config_address = config_pda().0;
618    let auction_address = auction_pda().0;
619    
620    Instruction {
621        program_id: crate::ID,
622        accounts: vec![
623            AccountMeta::new(signer, true),
624            AccountMeta::new_readonly(config_address, false),
625            AccountMeta::new(auction_address, false),
626        ],
627        data: SetAuction {
628            halving_period_seconds: halving_period_seconds.to_le_bytes(),
629            last_halving_time: last_halving_time.to_le_bytes(),
630            base_mining_rates: [
631                base_mining_rates[0].to_le_bytes(),
632                base_mining_rates[1].to_le_bytes(),
633                base_mining_rates[2].to_le_bytes(),
634                base_mining_rates[3].to_le_bytes(),
635            ],
636            auction_duration_seconds: auction_duration_seconds.to_le_bytes(),
637            starting_prices: [
638                starting_prices[0].to_le_bytes(),
639                starting_prices[1].to_le_bytes(),
640                starting_prices[2].to_le_bytes(),
641                starting_prices[3].to_le_bytes(),
642            ],
643            well_id: 4u64.to_le_bytes(), // Always use 4 to indicate auction-only update
644        }
645        .to_bytes(),
646    }
647}
648
649// let [signer_info, mint_info, sender_info, stake_info, stake_tokens_info, treasury_info, system_program, token_program, associated_token_program] =
650
651pub fn deposit(signer: Pubkey, authority: Pubkey, amount: u64, lock_duration_days: u64, stake_id: u64) -> Instruction {
652    let mint_address = MINT_ADDRESS;
653    let stake_address = stake_pda_with_id(authority, stake_id).0; // Derive from authority, not signer
654    let stake_tokens_address = get_associated_token_address(&stake_address, &MINT_ADDRESS);
655    let sender_address = get_associated_token_address(&authority, &MINT_ADDRESS); // Authority's ATA
656    let pool_address = pool_pda().0;
657    let pool_tokens_address = pool_tokens_address();
658    let miner_address = miner_pda(authority).0; // Derive from authority
659    Instruction {
660        program_id: crate::ID,
661        accounts: vec![
662            AccountMeta::new(signer, true), // payer (session payer or regular wallet, pays fees)
663            AccountMeta::new(authority, true), // authority (user's wallet, signs token transfer and used for PDA derivation)
664            AccountMeta::new(mint_address, false),
665            AccountMeta::new(sender_address, false),
666            AccountMeta::new(stake_address, false),
667            AccountMeta::new(stake_tokens_address, false),
668            AccountMeta::new(pool_address, false),
669            AccountMeta::new(pool_tokens_address, false),
670            AccountMeta::new(miner_address, false),
671            AccountMeta::new_readonly(system_program::ID, false),
672            AccountMeta::new_readonly(spl_token::ID, false),
673            AccountMeta::new_readonly(spl_associated_token_account::ID, false),
674        ],
675        data: Deposit {
676            amount: amount.to_le_bytes(),
677            lock_duration_days: lock_duration_days.to_le_bytes(),
678            stake_id: stake_id.to_le_bytes(),
679        }
680        .to_bytes(),
681    }
682}
683
684// let [signer_info, mint_info, recipient_info, stake_info, stake_tokens_info, treasury_info, system_program, token_program, associated_token_program] =
685
686pub fn withdraw(signer: Pubkey, authority: Pubkey, amount: u64, stake_id: u64) -> Instruction {
687    let stake_address = stake_pda_with_id(authority, stake_id).0; // Derive from authority, not signer
688    let stake_tokens_address = get_associated_token_address(&stake_address, &MINT_ADDRESS);
689    let mint_address = MINT_ADDRESS;
690    let recipient_address = get_associated_token_address(&authority, &MINT_ADDRESS); // Authority's ATA
691    let pool_address = pool_pda().0;
692    let pool_tokens_address = pool_tokens_address();
693    let miner_address = miner_pda(authority).0; // Derive from authority
694    Instruction {
695        program_id: crate::ID,
696        accounts: vec![
697            AccountMeta::new(signer, true), // payer (session payer or regular wallet)
698            AccountMeta::new(authority, false), // authority (user's wallet, for PDA derivation)
699            AccountMeta::new(mint_address, false),
700            AccountMeta::new(recipient_address, false),
701            AccountMeta::new(stake_address, false),
702            AccountMeta::new(stake_tokens_address, false),
703            AccountMeta::new(pool_address, false),
704            AccountMeta::new(pool_tokens_address, false),
705            AccountMeta::new(miner_address, false),
706            AccountMeta::new_readonly(system_program::ID, false),
707            AccountMeta::new_readonly(spl_token::ID, false),
708            AccountMeta::new_readonly(spl_associated_token_account::ID, false),
709        ],
710        data: Withdraw {
711            amount: amount.to_le_bytes(),
712            stake_id: stake_id.to_le_bytes(),
713        }
714        .to_bytes(),
715    }
716}
717
718// let [signer_info, automation_info, miner_info, system_program] = accounts else {
719
720/// Reload SOL from miner account to automation balance with single-tier referral system.
721/// 
722/// If the miner has a referrer, 1.0% of the claim goes to the referrer.
723/// 
724/// Account structure:
725/// - Base: signer, automation, miner, system_program
726/// - If miner has referrer (required): [miner_referrer, referral_referrer]
727pub fn reload_sol(
728    signer: Pubkey,
729    authority: Pubkey,
730    referrer_miner: Option<Pubkey>,
731    referrer_referral: Option<Pubkey>,
732) -> Instruction {
733    let automation_address = automation_pda(authority).0;
734    let miner_address = miner_pda(authority).0;
735    
736    let mut accounts = vec![
737        AccountMeta::new(signer, true),
738        AccountMeta::new(automation_address, false),
739        AccountMeta::new(miner_address, false),
740        AccountMeta::new_readonly(system_program::ID, false),
741    ];
742    
743    // Add referral accounts if provided (required when miner has referrer)
744    if let (Some(miner_ref), Some(referral_ref)) = (referrer_miner, referrer_referral) {
745        accounts.push(AccountMeta::new(miner_ref, false));
746        accounts.push(AccountMeta::new(referral_ref, false));
747    }
748    
749    Instruction {
750        program_id: crate::ID,
751        accounts,
752        data: ReloadSOL {}.to_bytes(),
753    }
754}
755
756// let [signer_info, mint_info, recipient_info, stake_info, treasury_info, treasury_tokens_info, system_program, token_program, associated_token_program] =
757
758/// Claim SOL yield from staking. Stakers earn SOL rewards (2% of round winnings), not OIL.
759pub fn claim_yield(signer: Pubkey, authority: Pubkey, amount: u64, stake_id: u64) -> Instruction {
760    let stake_address = stake_pda_with_id(authority, stake_id).0; // Derive from authority, not signer
761    let pool_address = pool_pda().0;
762    Instruction {
763        program_id: crate::ID,
764        accounts: vec![
765            AccountMeta::new(signer, true), // payer (session payer or regular wallet)
766            AccountMeta::new(authority, true), // authority (user's wallet, receives SOL and used for PDA derivation)
767            AccountMeta::new(stake_address, false),
768            AccountMeta::new(pool_address, false),
769            AccountMeta::new_readonly(system_program::ID, false),
770        ],
771        data: ClaimYield {
772            amount: amount.to_le_bytes(),
773        }
774        .to_bytes(),
775    }
776}
777
778pub fn new_var(
779    signer: Pubkey,
780    provider: Pubkey,
781    id: u64,
782    commit: [u8; 32],
783    samples: u64,
784) -> Instruction {
785    let board_address = board_pda().0;
786    let config_address = config_pda().0;
787    let var_address = entropy_rng_api::state::var_pda(board_address, id).0;
788    Instruction {
789        program_id: crate::ID,
790        accounts: vec![
791            AccountMeta::new(signer, true),
792            AccountMeta::new(board_address, false),
793            AccountMeta::new(config_address, false),
794            AccountMeta::new(provider, false),
795            AccountMeta::new(var_address, false),
796            AccountMeta::new_readonly(system_program::ID, false),
797            AccountMeta::new_readonly(entropy_rng_api::ID, false),
798        ],
799        data: NewVar {
800            id: id.to_le_bytes(),
801            commit: commit,
802            samples: samples.to_le_bytes(),
803        }
804        .to_bytes(),
805    }
806}
807
808pub fn set_swap_program(signer: Pubkey, new_program: Pubkey) -> Instruction {
809    let config_address = config_pda().0;
810    Instruction {
811        program_id: crate::ID,
812        accounts: vec![
813            AccountMeta::new(signer, true),
814            AccountMeta::new(config_address, false),
815            AccountMeta::new_readonly(new_program, false),
816        ],
817        data: SetSwapProgram {}.to_bytes(),
818    }
819}
820
821pub fn set_var_address(signer: Pubkey, new_var_address: Pubkey) -> Instruction {
822    let board_address = board_pda().0;
823    let config_address = config_pda().0;
824    Instruction {
825        program_id: crate::ID,
826        accounts: vec![
827            AccountMeta::new(signer, true),
828            AccountMeta::new(board_address, false),
829            AccountMeta::new(config_address, false),
830            AccountMeta::new(new_var_address, false),
831        ],
832        data: SetVarAddress {}.to_bytes(),
833    }
834}
835
836/// Migrate: Extend Config struct with tge_timestamp field.
837/// This migration ensures the Config account has the new tge_timestamp field available.
838/// Must be called by the admin.
839/// Accounts: signer, config, system_program
840pub fn migrate(signer: Pubkey) -> Instruction {
841    let config_address = config_pda().0;
842    Instruction {
843        program_id: crate::ID,
844        accounts: vec![
845            AccountMeta::new(signer, true),
846            AccountMeta::new(config_address, false),
847            AccountMeta::new_readonly(system_program::ID, false),
848        ],
849        data: Migrate {}.to_bytes(),
850    }
851}
852
853/// Create a referral account to become a referrer.
854pub fn create_referral(signer: Pubkey) -> Instruction {
855    let referral_address = referral_pda(signer).0;
856    Instruction {
857        program_id: crate::ID,
858        accounts: vec![
859            AccountMeta::new(signer, true),
860            AccountMeta::new(referral_address, false),
861            AccountMeta::new_readonly(system_program::ID, false),
862        ],
863        data: CreateReferral {}.to_bytes(),
864    }
865}
866
867/// Creates a Code account linking an access code to a specific wallet (authority).
868/// Admin-only instruction.
869/// Accounts: signer (admin), config, code, system_program
870pub fn create_code(
871    signer: Pubkey,
872    code_hash: [u8; 32],
873    authority: Pubkey,
874) -> Instruction {
875    let config_address = config_pda().0;
876    let (code_address, _) = Code::pda(code_hash, authority);
877    Instruction {
878        program_id: crate::ID,
879        accounts: vec![
880            AccountMeta::new(signer, true), // signer (admin)
881            AccountMeta::new_readonly(config_address, false), // config
882            AccountMeta::new(code_address, false), // code
883            AccountMeta::new_readonly(system_program::ID, false), // system_program
884        ],
885        data: CreateCode {
886            code_hash,
887            authority: authority.to_bytes(),
888        }
889        .to_bytes(),
890    }
891}
892
893/// Claim pending referral rewards (both SOL and OIL).
894/// 
895/// Account structure (for Fogo sessions):
896/// - Base: signer (payer), authority (user's wallet), referral, referral_tokens, mint, recipient, system_program, token_program, associated_token_program
897pub fn claim_referral(signer: Pubkey, authority: Pubkey) -> Instruction {
898    let referral_address = referral_pda(authority).0;
899    let referral_oil_address = get_associated_token_address(&referral_address, &MINT_ADDRESS);
900    let recipient_oil_address = get_associated_token_address(&authority, &MINT_ADDRESS);
901    Instruction {
902        program_id: crate::ID,
903        accounts: vec![
904            AccountMeta::new(signer, true), // 0: signer (payer)
905            AccountMeta::new(authority, false), // 1: authority (user's wallet, receives SOL)
906            AccountMeta::new(referral_address, false), // 2: referral
907            AccountMeta::new(referral_oil_address, false), // 3: referral_tokens (Referral account's OIL ATA)
908            AccountMeta::new(MINT_ADDRESS, false), // 4: mint
909            AccountMeta::new(recipient_oil_address, false), // 5: recipient (Recipient's OIL ATA - authority's wallet)
910            AccountMeta::new_readonly(system_program::ID, false), // 6: system_program
911            AccountMeta::new_readonly(spl_token::ID, false), // 7: token_program
912            AccountMeta::new_readonly(spl_associated_token_account::ID, false), // 8: associated_token_program
913        ],
914        data: ClaimReferral {}.to_bytes(),
915    }
916}
917
918/// Direct solo bid on an auction well (seize ownership).
919/// The bid amount is calculated on-chain as current_price + 1 lamport.
920/// User must have enough SOL in their wallet to cover the bid.
921/// 
922/// Account structure:
923/// - Base: signer, authority, program_signer (optional), payer (optional), well, auction, treasury, treasury_tokens, mint, mint_authority, mint_program, staking_pool, fee_collector, config, token_program, system_program, oil_program
924/// - If previous owner exists (optional): [previous_owner_miner, previous_owner]
925/// - If referrer is provided (optional): [referral]
926/// - If access_code_hash is provided (optional): [code]
927pub fn place_bid(
928    signer: Pubkey,
929    authority: Pubkey,
930    square_id: u64,
931    fee_collector: Pubkey,
932    previous_owner_miner: Option<Pubkey>, // Previous owner's miner PDA (if previous owner exists)
933    previous_owner: Option<Pubkey>, // Previous owner pubkey (if previous owner exists)
934    referrer: Option<Pubkey>, // Optional referrer pubkey for new miners
935    access_code_hash: Option<[u8; 32]>, // Optional access code hash for pre-mine
936) -> Instruction {
937    let well_address = well_pda(square_id).0;
938    let auction_address = auction_pda().0;
939    let treasury_address = treasury_pda().0;
940    let treasury_tokens_address = get_associated_token_address(&treasury_address, &MINT_ADDRESS);
941    let staking_pool_address = pool_pda().0;
942    let config_address = config_pda().0;
943    let mint_authority_address = oil_mint_api::state::authority_pda().0;
944    let bidder_miner_address = miner_pda(authority).0;
945    
946    let mut accounts = vec![
947        AccountMeta::new(signer, true), // 0: signer
948        AccountMeta::new(authority, false), // 1: authority
949    ];
950    
951    // Add program_signer and payer for Fogo sessions (these are added by the client, not here)
952    // For now, we'll add placeholders or the client will add them
953    
954    accounts.extend_from_slice(&[
955        AccountMeta::new(well_address, false), // well
956        AccountMeta::new(auction_address, false), // Must be writable for auction_program_log CPI
957        AccountMeta::new(treasury_address, false),
958        AccountMeta::new(treasury_tokens_address, false),
959        AccountMeta::new(MINT_ADDRESS, false),
960        AccountMeta::new(mint_authority_address, false),
961        AccountMeta::new_readonly(oil_mint_api::ID, false),
962        AccountMeta::new(staking_pool_address, false),
963        AccountMeta::new(fee_collector, false),
964        AccountMeta::new_readonly(config_address, false),
965        AccountMeta::new_readonly(spl_token::ID, false),
966        AccountMeta::new_readonly(system_program::ID, false),
967        AccountMeta::new_readonly(crate::ID, false), // oil_program
968        AccountMeta::new(bidder_miner_address, false), // bidder_miner
969    ]);
970    
971    // Add previous owner accounts if provided
972    if let (Some(miner_pubkey), Some(owner_pubkey)) = (previous_owner_miner, previous_owner) {
973        accounts.push(AccountMeta::new(miner_pubkey, false)); // previous_owner_miner
974        accounts.push(AccountMeta::new(owner_pubkey, false)); // previous_owner
975    }
976    
977    // Add referral account if referrer is provided
978    if let Some(referrer_pubkey) = referrer {
979        let referral_address = referral_pda(referrer_pubkey).0;
980        accounts.push(AccountMeta::new(referral_address, false)); // referral
981    }
982    
983    // Add Code account if access_code_hash is provided
984    let access_code_hash_bytes = access_code_hash.unwrap_or([0; 32]);
985    if access_code_hash_bytes != [0; 32] {
986        let (code_address, _) = Code::pda(access_code_hash_bytes, authority);
987        accounts.push(AccountMeta::new(code_address, false)); // code (optional)
988    }
989    
990    // Note: Wrapped token accounts are added by the client:
991    // - user_wrapped_sol (source)
992    // - treasury_wrapped_sol (temp ATA for all wrapped SOL - will be closed to get native SOL)
993    // - token_program, native_mint, ata_program
994    // The program distributes native SOL from treasury to pool and fee_collector after closing the temp ATA.
995    
996    Instruction {
997        program_id: crate::ID,
998        accounts,
999        data: instruction::PlaceBid {
1000            square_id: square_id.to_le_bytes(),
1001            referrer: referrer.unwrap_or(Pubkey::default()).to_bytes(),
1002            access_code_hash: access_code_hash_bytes,
1003        }
1004        .to_bytes(),
1005    }
1006}
1007
1008/// Claim auction-based OIL rewards
1009/// - OIL rewards: from current ownership and previous ownership (pre-minted)
1010/// 
1011/// Account structure:
1012/// - Base: signer, miner, well accounts (one per well in mask), auction pool accounts (optional, one per well), auction, treasury, treasury_tokens, mint, mint_authority, mint_program, recipient, token_program, associated_token_program, system_program, oil_program
1013/// - Bid accounts (one per well in mask, required for pool contributors): [bid_0, bid_1, bid_2, bid_3] (must include epoch_id in PDA)
1014pub fn claim_auction_oil(
1015    signer: Pubkey,
1016    well_mask: u8, // Bitmask: bit 0 = well 0, bit 1 = well 1, etc.
1017    well_accounts: [Option<Pubkey>; 4], // Well PDAs for wells 0-3 (required for wells in mask)
1018    auction_pool_accounts: Option<[Option<Pubkey>; 4]>, // Auction Pool PDAs for wells 0-3 (optional, for pool contributors)
1019    bid_accounts: Option<[Option<Pubkey>; 4]>, // Bid PDAs for wells 0-3 (required for pool contributors, must include epoch_id in PDA)
1020) -> Instruction {
1021    let miner_address = miner_pda(signer).0;
1022    let auction_address = auction_pda().0;
1023    let treasury_address = treasury_pda().0;
1024    let treasury_tokens_address = get_associated_token_address(&treasury_address, &MINT_ADDRESS);
1025    let recipient_address = get_associated_token_address(&signer, &MINT_ADDRESS);
1026    let mint_authority_address = oil_mint_api::state::authority_pda().0;
1027    
1028    let mut accounts = vec![
1029        AccountMeta::new(signer, true),
1030        AccountMeta::new(miner_address, false),
1031    ];
1032    
1033    // Add well accounts for wells in mask (all 4 required, but only wells in mask are used)
1034    for well_opt in well_accounts.iter() {
1035        if let Some(well_pubkey) = well_opt {
1036            accounts.push(AccountMeta::new(*well_pubkey, false));
1037        } else {
1038            // Use a placeholder account if well is not provided
1039            accounts.push(AccountMeta::new_readonly(system_program::ID, false));
1040        }
1041    }
1042    
1043    // Add auction pool accounts if provided (optional, for pool contributors)
1044    if let Some(auction_pool_pdas) = auction_pool_accounts {
1045        for auction_pool_pda_opt in auction_pool_pdas.iter() {
1046            if let Some(auction_pool_pubkey) = auction_pool_pda_opt {
1047                accounts.push(AccountMeta::new(*auction_pool_pubkey, false));
1048            } else {
1049                // Use a placeholder account if auction pool is not provided
1050                accounts.push(AccountMeta::new_readonly(system_program::ID, false));
1051            }
1052        }
1053    } else {
1054        // Add 4 placeholder accounts if auction_pool_accounts is None
1055        for _ in 0..4 {
1056            accounts.push(AccountMeta::new_readonly(system_program::ID, false));
1057        }
1058    }
1059    
1060    accounts.extend_from_slice(&[
1061        AccountMeta::new(auction_address, false), // Must be writable for auction_program_log CPI
1062        AccountMeta::new(treasury_address, false),
1063        AccountMeta::new(treasury_tokens_address, false),
1064        AccountMeta::new(MINT_ADDRESS, false), // Must be writable for mint_oil CPI
1065        AccountMeta::new(mint_authority_address, false), // Must be writable for mint_oil CPI
1066        AccountMeta::new_readonly(oil_mint_api::ID, false),
1067        AccountMeta::new(recipient_address, false),
1068        AccountMeta::new_readonly(spl_token::ID, false),
1069        AccountMeta::new_readonly(spl_associated_token_account::ID, false),
1070        AccountMeta::new_readonly(system_program::ID, false),
1071        AccountMeta::new_readonly(crate::ID, false), // oil_program
1072    ]);
1073    
1074    // Add bid accounts if provided (for pool contributors)
1075    if let Some(bid_pdas) = bid_accounts {
1076        for bid_pda_opt in bid_pdas.iter() {
1077            if let Some(bid_pubkey) = bid_pda_opt {
1078                accounts.push(AccountMeta::new(*bid_pubkey, false));
1079            }
1080        }
1081    }
1082    
1083    Instruction {
1084        program_id: crate::ID,
1085        accounts,
1086        data: ClaimAuctionOIL {
1087            well_mask,
1088        }
1089        .to_bytes(),
1090    }
1091}
1092
1093/// Claim auction-based SOL rewards
1094/// - SOL rewards: from being outbid and refunds from abandoned pools
1095/// 
1096/// Account structure:
1097/// - Base: signer, miner, well accounts (one per well 0-3, for checking abandoned pools), auction pool accounts (optional, one per well), auction, treasury, system_program, oil_program
1098/// - Bid accounts (one per well, required for refunds): [bid_0, bid_1, bid_2, bid_3] (must include epoch_id in PDA)
1099pub fn claim_auction_sol(
1100    signer: Pubkey,
1101    well_accounts: [Option<Pubkey>; 4], // Well PDAs for wells 0-3 (for checking abandoned pools)
1102    auction_pool_accounts: Option<[Option<Pubkey>; 4]>, // Auction Pool PDAs for wells 0-3 (optional, for refunds)
1103    bid_accounts: Option<[Option<Pubkey>; 4]>, // Bid PDAs for wells 0-3 (required for refunds, must include epoch_id in PDA)
1104) -> Instruction {
1105    let miner_address = miner_pda(signer).0;
1106    let (auction_address, _) = auction_pda();
1107    let treasury_address = treasury_pda().0;
1108    
1109    let mut accounts = vec![
1110        AccountMeta::new(signer, true),
1111        AccountMeta::new(miner_address, false),
1112    ];
1113    
1114    // Add well accounts (all 4 wells for checking abandoned pools)
1115    for well_opt in well_accounts.iter() {
1116        if let Some(well_pubkey) = well_opt {
1117            accounts.push(AccountMeta::new(*well_pubkey, false));
1118        } else {
1119            // Use a placeholder account if well is not provided
1120            accounts.push(AccountMeta::new_readonly(system_program::ID, false));
1121        }
1122    }
1123    
1124    // Add auction pool accounts if provided (optional, for refunds)
1125    if let Some(auction_pool_pdas) = auction_pool_accounts {
1126        for auction_pool_pda_opt in auction_pool_pdas.iter() {
1127            if let Some(auction_pool_pubkey) = auction_pool_pda_opt {
1128                accounts.push(AccountMeta::new(*auction_pool_pubkey, false));
1129            } else {
1130                // Use a placeholder account if auction pool is not provided
1131                accounts.push(AccountMeta::new_readonly(system_program::ID, false));
1132            }
1133        }
1134    } else {
1135        // Add 4 placeholder accounts if auction_pool_accounts is None
1136        for _ in 0..4 {
1137            accounts.push(AccountMeta::new_readonly(system_program::ID, false));
1138        }
1139    }
1140    
1141    accounts.extend_from_slice(&[
1142        AccountMeta::new(auction_address, false), // Must be writable for auction_program_log CPI
1143        AccountMeta::new(treasury_address, false),
1144        AccountMeta::new_readonly(system_program::ID, false),
1145        AccountMeta::new_readonly(crate::ID, false), // oil_program
1146    ]);
1147    
1148    // Add bid accounts if provided (for refunds)
1149    if let Some(bid_pdas) = bid_accounts {
1150        for bid_pda_opt in bid_pdas.iter() {
1151            if let Some(bid_pubkey) = bid_pda_opt {
1152                accounts.push(AccountMeta::new(*bid_pubkey, false));
1153            }
1154        }
1155    }
1156    
1157    Instruction {
1158        program_id: crate::ID,
1159        accounts,
1160        data: ClaimAuctionSOL {
1161            _reserved: 0,
1162        }
1163        .to_bytes(),
1164    }
1165}
1166
1167pub fn claim_seeker(signer: Pubkey, mint: Pubkey) -> Instruction {
1168    let seeker_address = seeker_pda(mint).0;
1169    let token_account_address = get_associated_token_address(&signer, &mint);
1170    Instruction {
1171        program_id: crate::ID,
1172        accounts: vec![
1173            AccountMeta::new(signer, true),
1174            AccountMeta::new_readonly(mint, false),
1175            AccountMeta::new(seeker_address, false),
1176            AccountMeta::new(token_account_address, false),
1177            AccountMeta::new_readonly(system_program::ID, false),
1178        ],
1179        data: ClaimSeeker {}.to_bytes(),
1180    }
1181}