Skip to main content

antegen_thread_program/instructions/
fiber_update.rs

1use crate::{errors::*, *};
2use anchor_lang::prelude::*;
3use antegen_fiber_program::{
4    program::AntegenFiber,
5    state::SerializableInstruction,
6};
7
8/// Accounts required by the `fiber_update` instruction.
9/// Validates authority, CPIs to Fiber Program to update (or init) the fiber.
10/// Thread PDA pays for fiber init if needed.
11#[derive(Accounts)]
12#[instruction(fiber_index: u8)]
13pub struct FiberUpdate<'info> {
14    /// The authority of the thread or the thread itself
15    #[account(
16        constraint = authority.key().eq(&thread.authority) || authority.key().eq(&thread.key())
17    )]
18    pub authority: Signer<'info>,
19
20    /// The thread the fiber belongs to
21    #[account(
22        mut,
23        seeds = [SEED_THREAD, thread.authority.as_ref(), thread.id.as_slice()],
24        bump = thread.bump,
25    )]
26    pub thread: Account<'info, Thread>,
27
28    /// CHECK: The fiber account to update (may not exist yet, validated by Fiber Program via CPI)
29    #[account(mut)]
30    pub fiber: UncheckedAccount<'info>,
31
32    /// The Fiber Program for CPI
33    pub fiber_program: Program<'info, AntegenFiber>,
34
35    pub system_program: Program<'info, System>,
36}
37
38pub fn fiber_update(
39    ctx: Context<FiberUpdate>,
40    fiber_index: u8,
41    instruction: Option<SerializableInstruction>,
42    priority_fee: Option<u64>,
43    track: bool,
44) -> Result<()> {
45    // Prevent thread_delete instructions in fibers
46    if let Some(ref ix) = instruction {
47        if ix.program_id.eq(&crate::ID)
48            && ix.data.len().ge(&8)
49            && ix.data[..8].eq(crate::instruction::DeleteThread::DISCRIMINATOR)
50        {
51            return Err(AntegenThreadError::InvalidInstruction.into());
52        }
53    }
54
55    let thread = &mut ctx.accounts.thread;
56
57    // Track the fiber in the thread's fiber_ids before CPI
58    if track && !thread.fiber_ids.contains(&fiber_index) {
59        thread.fiber_ids.push(fiber_index);
60        thread.fiber_ids.sort();
61        if fiber_index.ge(&thread.fiber_next_id) {
62            thread.fiber_next_id = fiber_index.saturating_add(1);
63        }
64    }
65
66    // Pre-fund fiber account from thread PDA if not yet initialized
67    let fiber_info = ctx.accounts.fiber.to_account_info();
68    if fiber_info.data_len().eq(&0) {
69        let space = 8 + antegen_fiber_program::state::FiberState::INIT_SPACE;
70        let rent_lamports = Rent::get()?.minimum_balance(space);
71        **thread.to_account_info().try_borrow_mut_lamports()? -= rent_lamports;
72        **fiber_info.try_borrow_mut_lamports()? += rent_lamports;
73    }
74
75    // CPI to Fiber Program's update_fiber
76    thread.sign(|signer| {
77        antegen_fiber_program::cpi::update(
78            CpiContext::new_with_signer(
79                ctx.accounts.fiber_program.key(),
80                antegen_fiber_program::cpi::accounts::Update {
81                    thread: thread.to_account_info(),
82                    fiber: ctx.accounts.fiber.to_account_info(),
83                    system_program: ctx.accounts.system_program.to_account_info(),
84                },
85                &[signer],
86            ),
87            fiber_index,
88            instruction,
89            priority_fee,
90        )
91    })?;
92
93    Ok(())
94}