Skip to main content

antegen_fiber_program/instructions/
fiber_create.rs

1use crate::constants::*;
2use crate::errors::AntegenFiberError;
3use crate::state::*;
4use anchor_lang::prelude::*;
5use anchor_lang::solana_program::instruction::Instruction;
6use anchor_lang::solana_program::program::invoke_signed;
7use anchor_lang::solana_program::system_instruction;
8
9/// Accounts required by the `create_fiber` instruction.
10/// Thread PDA is the signer (authority). Fiber must be pre-funded with rent lamports.
11#[derive(Accounts)]
12#[instruction(fiber_index: u8)]
13pub struct FiberCreate<'info> {
14    /// Thread PDA - signer (via invoke_signed from Thread Program)
15    pub thread: Signer<'info>,
16
17    /// CHECK: The fiber account to create — validated manually via PDA derivation
18    #[account(mut)]
19    pub fiber: UncheckedAccount<'info>,
20
21    pub system_program: Program<'info, System>,
22}
23
24pub fn fiber_create(
25    ctx: Context<FiberCreate>,
26    fiber_index: u8,
27    instruction: Instruction,
28    priority_fee: u64,
29) -> Result<()> {
30    let thread_key = ctx.accounts.thread.key();
31    let fiber_info = ctx.accounts.fiber.to_account_info();
32
33    if fiber_info.data_len() == 0 {
34        // Not initialized — full init
35        initialize_fiber(
36            &ctx.accounts.fiber,
37            &ctx.accounts.system_program,
38            &thread_key,
39            fiber_index,
40            &instruction,
41            priority_fee,
42        )
43    } else {
44        // Already initialized — update in place (same as fiber_update)
45        let mut data = fiber_info.try_borrow_mut_data()?;
46        let discriminator = FiberState::DISCRIMINATOR;
47        if data[..8] != discriminator[..] {
48            return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch.into());
49        }
50
51        let compiled = compile_instruction(instruction)?;
52        let compiled_bytes = borsh::to_vec(&compiled)?;
53
54        let mut state: FiberState = FiberState::try_deserialize(&mut &data[..])?;
55        state.thread = thread_key;
56        state.compiled_instruction = compiled_bytes;
57        state.priority_fee = priority_fee;
58        state.last_executed = 0;
59        state.exec_count = 0;
60
61        let state_bytes = borsh::to_vec(&state)?;
62        data[8..8 + state_bytes.len()].copy_from_slice(&state_bytes);
63        Ok(())
64    }
65}
66
67/// Shared helper for manual fiber account initialization.
68/// Derives PDA with known fiber_index (single find_program_address call, same as Anchor),
69/// validates key match, checks not already initialized, allocates + assigns via invoke_signed,
70/// then writes discriminator + state.
71pub fn initialize_fiber<'info>(
72    fiber: &UncheckedAccount<'info>,
73    system_program: &Program<'info, System>,
74    thread_key: &Pubkey,
75    fiber_index: u8,
76    instruction: &Instruction,
77    priority_fee: u64,
78) -> Result<()> {
79    let fiber_info = fiber.to_account_info();
80
81    // Derive PDA with known seeds — single call, same as Anchor does
82    let (expected_pda, bump) = Pubkey::find_program_address(
83        &[SEED_THREAD_FIBER, thread_key.as_ref(), &[fiber_index]],
84        &crate::ID,
85    );
86    require!(
87        expected_pda.eq(&fiber.key()),
88        AntegenFiberError::InvalidFiberPDA
89    );
90
91    // Verify rent
92    let space = 8 + FiberState::INIT_SPACE;
93    let rent = Rent::get()?;
94    let min_lamports = rent.minimum_balance(space);
95    require!(
96        fiber_info.lamports().ge(&min_lamports),
97        AntegenFiberError::InsufficientRent
98    );
99
100    // Allocate space via invoke_signed (fiber PDA is derived from fiber program)
101    let seeds: &[&[u8]] = &[
102        SEED_THREAD_FIBER,
103        thread_key.as_ref(),
104        &[fiber_index],
105        &[bump],
106    ];
107
108    invoke_signed(
109        &system_instruction::allocate(&fiber.key(), space as u64),
110        &[fiber_info.clone(), system_program.to_account_info()],
111        &[seeds],
112    )?;
113
114    // Assign to this program
115    invoke_signed(
116        &system_instruction::assign(&fiber.key(), &crate::ID),
117        &[fiber_info.clone(), system_program.to_account_info()],
118        &[seeds],
119    )?;
120
121    // Write discriminator + state
122    let compiled = compile_instruction(instruction.clone())?;
123    let compiled_bytes = borsh::to_vec(&compiled)?;
124
125    let state = FiberState {
126        thread: *thread_key,
127        compiled_instruction: compiled_bytes,
128        priority_fee,
129        last_executed: 0,
130        exec_count: 0,
131    };
132
133    // Write anchor discriminator
134    let mut data = fiber_info.try_borrow_mut_data()?;
135    let discriminator = FiberState::DISCRIMINATOR;
136    data[..8].copy_from_slice(discriminator);
137
138    // Serialize state after discriminator
139    let state_bytes = borsh::to_vec(&state)?;
140    data[8..8 + state_bytes.len()].copy_from_slice(&state_bytes);
141
142    Ok(())
143}