oil_api/
utils.rs

1use solana_program::program_error::ProgramError;
2use solana_program::program::invoke;
3use spl_associated_token_account::instruction::create_associated_token_account_idempotent;
4use steel::*;
5
6/// Creates a wrapped SOL ATA if it doesn't exist, otherwise validates it.
7pub fn create_or_validate_wrapped_sol_ata<'a>(
8    ata_info: &AccountInfo<'a>,
9    owner_info: &AccountInfo<'a>,
10    mint_info: &AccountInfo<'a>,
11    payer_info: &AccountInfo<'a>,
12    system_program: &AccountInfo<'a>,
13    token_program: &AccountInfo<'a>,
14    ata_program: &AccountInfo<'a>,
15    _log_message: Option<&str>,
16) -> Result<(), ProgramError> {
17    ata_info.is_writable()?;
18    
19    // CRITICAL: Check if account is already owned by token program FIRST
20    // If it is, it was created by a previous instruction (e.g., session wrap)
21    // and we must NOT try to create it again (this causes "Transfer: from must not carry data" error)
22    let is_empty = ata_info.data_is_empty();
23    // Only check ownership if account is not empty (has_owner fails on empty accounts)
24    let is_owned_by_token_program = !is_empty && ata_info.has_owner(token_program.key).is_ok();
25    
26    if is_owned_by_token_program {
27        // Account is owned by token program - it exists, just validate it
28        // Don't try to create it, even if validation initially fails
29        ata_info.as_associated_token_account(owner_info.key, mint_info.key).map(|_| ())
30    } else if is_empty {
31        // Account doesn't exist - create it using idempotent instruction
32        let create_ix = create_associated_token_account_idempotent(
33            payer_info.key,
34            owner_info.key,
35            mint_info.key,
36            token_program.key,
37        );
38        
39        invoke(
40            &create_ix,
41            &[
42                payer_info.clone(),
43                ata_info.clone(),
44                owner_info.clone(),
45                mint_info.clone(),
46                system_program.clone(),
47                token_program.clone(),
48                ata_program.clone(),
49            ],
50        )?;
51        
52        // After creation, validate to ensure it's correct
53        ata_info.as_associated_token_account(owner_info.key, mint_info.key)?;
54        Ok(())
55    } else {
56        // Account has data but is not owned by token program
57        // Try to validate - if it fails, it's an error
58        ata_info.as_associated_token_account(owner_info.key, mint_info.key).map(|_| ())
59    }
60}