antegen_thread_program/instructions/
thread_error.rs1use crate::{errors::*, state::PaymentDetails, *};
2use anchor_lang::prelude::*;
3
4#[derive(Accounts)]
6pub struct ThreadError<'info> {
7 #[account(mut)]
9 pub executor: Signer<'info>,
10
11 #[account(
13 mut,
14 seeds = [
15 SEED_THREAD,
16 thread.authority.as_ref(),
17 thread.id.as_slice(),
18 ],
19 bump = thread.bump,
20 constraint = !thread.paused @ AntegenThreadError::ThreadPaused,
21 )]
22 pub thread: Box<Account<'info, Thread>>,
23
24 #[account(
26 seeds = [SEED_CONFIG],
27 bump = config.bump,
28 )]
29 pub config: Account<'info, ThreadConfig>,
30
31 #[account(
34 mut,
35 constraint = admin.key().eq(&config.admin) @ AntegenThreadError::InvalidConfigAdmin,
36 )]
37 pub admin: UncheckedAccount<'info>,
38
39 #[account(address = anchor_lang::system_program::ID)]
40 pub system_program: Program<'info, System>,
41}
42
43pub fn thread_error(
44 ctx: Context<ThreadError>,
45 error_code: u32,
46 error_message: String,
47) -> Result<()> {
48 let clock = Clock::get()?;
49 let thread = &mut ctx.accounts.thread;
50 let config = &ctx.accounts.config;
51 let executor = &ctx.accounts.executor;
52
53 let executor_lamports_start = executor.lamports();
55
56 let last_executor_is_default = thread.last_executor == Pubkey::default();
59 let we_were_last_executor = thread.last_executor == executor.key();
60
61 require!(
62 last_executor_is_default || we_were_last_executor,
63 AntegenThreadError::NotLastExecutor
64 );
65
66 require!(
68 thread.last_error_time.is_none(),
69 AntegenThreadError::ErrorAlreadyReported
70 );
71
72 let thread_pubkey = thread.key();
75 let time_since_ready =
76 thread.validate_trigger(&clock, ctx.remaining_accounts, &thread_pubkey)?;
77 let error_threshold = config.grace_period_seconds + config.fee_decay_seconds;
78
79 require!(
80 time_since_ready >= error_threshold,
81 AntegenThreadError::ThreadNotSufficientlyOverdue
82 );
83
84 const ERROR_REIMBURSEMENT: u64 = 10_000;
86
87 let rent_sysvar = Rent::get()?;
88 let available_lamports = thread
89 .to_account_info()
90 .lamports()
91 .saturating_sub(rent_sysvar.minimum_balance(thread.to_account_info().data_len()));
92
93 let payments = PaymentDetails {
94 fee_payer_reimbursement: ERROR_REIMBURSEMENT.min(available_lamports),
95 executor_commission: 0,
96 core_team_fee: 0,
97 };
98
99 thread.distribute_payments(
101 &thread.to_account_info(),
102 &executor.to_account_info(),
103 &ctx.accounts.admin.to_account_info(),
104 &payments,
105 )?;
106
107 thread.last_error_time = Some(clock.unix_timestamp);
109
110 msg!(
112 "ANTEGEN_ERROR: thread={}, executor={}, code={}, message={}, overdue_by={}s",
113 thread.key(),
114 executor.key(),
115 error_code,
116 error_message,
117 time_since_ready
118 );
119
120 let balance_change = executor.lamports() as i64 - executor_lamports_start as i64;
122 require!(balance_change >= 0, AntegenThreadError::PaymentFailed);
123
124 Ok(())
125}