sablier_thread_program/instructions/
thread_delete.rs

1use {
2    crate::{constants::SEED_THREAD, errors::SablierError, state::*},
3    anchor_lang::{prelude::*, solana_program::system_program},
4};
5
6/// Accounts required by the `thread_delete` instruction.
7#[derive(Accounts)]
8pub struct ThreadDelete<'info> {
9    /// The authority (owner) of the thread.
10    pub authority: Signer<'info>,
11
12    /// The address to return the data rent lamports to.
13    #[account(mut)]
14    pub close_to: SystemAccount<'info>,
15
16    /// The thread to be deleted.
17    /// CHECK: Validation checks are performed during instruction processing.
18    #[account(mut)]
19    pub thread: UncheckedAccount<'info>,
20}
21
22pub fn handler(ctx: Context<ThreadDelete>) -> Result<()> {
23    let thread = &ctx.accounts.thread;
24    let close_to = &ctx.accounts.close_to;
25
26    // We want this instruction not to fail if the thread is already deleted or inexistent.
27    // As such, all checks are done in the code that than in anchor (see commented code above)
28    // First, must try to deserialize the thread.
29
30    // Get either V1 or V2 thread - If the provided thread does not exist, print an error message and return Ok.
31    let thread = match Thread::try_deserialize_unchecked(&mut thread.data.borrow_mut().as_ref()) {
32        Ok(t) => t,
33        Err(_) => {
34            msg!("Not a thread or account does not exist");
35            return Ok(());
36        }
37    };
38
39    // Preliminary checks
40    {
41        // Verify the authority
42        let authority_key = ctx.accounts.authority.key;
43        let thread_key = ctx.accounts.thread.key;
44
45        require!(
46            thread.authority.eq(authority_key) || authority_key.eq(thread_key),
47            SablierError::InvalidThreadAuthority
48        );
49
50        // Verify the account provided
51        let thread_account = &ctx.accounts.thread;
52        {
53            // Verify the account is initialized
54            require!(
55                thread_account.owner != &system_program::ID && thread_account.lamports() > 0,
56                SablierError::InvalidThreadAccount
57            );
58
59            // Verify the account is owned by the program
60            require!(
61                thread_account.owner == &crate::ID,
62                SablierError::InvalidThreadAccount
63            );
64
65            // Verify the seed derivation
66            let default_vec = Vec::new();
67            let thread_bump = thread.bump.to_le_bytes();
68            let seed = [
69                SEED_THREAD,
70                thread.authority.as_ref(),
71                thread.id.as_slice(),
72                thread.domain.as_ref().unwrap_or(&default_vec).as_slice(),
73                thread_bump.as_ref(),
74            ];
75            let expected_thread_key = Pubkey::create_program_address(&seed, &crate::ID)
76                .map_err(|_| SablierError::InvalidThreadAccount)?;
77            require!(
78                expected_thread_key == *thread_key,
79                SablierError::InvalidThreadAccount
80            );
81        }
82    }
83
84    // Transfer lamports out (implicit close)
85    {
86        let thread_account = &ctx.accounts.thread;
87        let thread_lamports = thread_account.get_lamports();
88        thread_account.sub_lamports(thread_lamports)?;
89        close_to.add_lamports(thread_lamports)?;
90    }
91    Ok(())
92}