Skip to main content

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