sablier_network_program/jobs/distribute_fees/
process_entry.rs

1use anchor_lang::{prelude::*, solana_program::instruction::Instruction, InstructionData};
2use sablier_utils::thread::ThreadResponse;
3
4use crate::{constants::*, state::*};
5
6#[derive(Accounts)]
7pub struct DistributeFeesProcessEntry<'info> {
8    #[account(address = Config::pubkey())]
9    pub config: AccountLoader<'info, Config>,
10
11    #[account(
12        mut,
13        seeds = [
14            SEED_DELEGATION,
15            delegation.worker.as_ref(),
16            delegation.id.to_be_bytes().as_ref(),
17        ],
18        bump,
19        constraint = delegation.id == snapshot_entry.id,
20        has_one = worker,
21    )]
22    pub delegation: Account<'info, Delegation>,
23
24    #[account(
25        mut,
26        seeds = [
27            SEED_FEE,
28            fee.worker.as_ref(),
29        ],
30        bump,
31        has_one = worker,
32    )]
33    pub fee: Account<'info, Fee>,
34
35    #[account(address = Registry::pubkey())]
36    pub registry: Account<'info, Registry>,
37
38    #[account(
39        address = snapshot.pubkey(),
40        constraint = snapshot.id == registry.current_epoch
41    )]
42    pub snapshot: Account<'info, Snapshot>,
43
44    #[account(
45        address = snapshot_entry.pubkey(),
46        has_one = snapshot_frame,
47    )]
48    pub snapshot_entry: Account<'info, SnapshotEntry>,
49
50    #[account(
51        address = snapshot_frame.pubkey(),
52        has_one = snapshot,
53        has_one = worker,
54    )]
55    pub snapshot_frame: Account<'info, SnapshotFrame>,
56
57    #[account(address = config.load()?.epoch_thread)]
58    pub thread: Signer<'info>,
59
60    #[account(address = worker.pubkey())]
61    pub worker: Account<'info, Worker>,
62}
63
64pub fn handler(ctx: Context<DistributeFeesProcessEntry>) -> Result<ThreadResponse> {
65    // Get accounts
66    let config = &ctx.accounts.config;
67    let delegation = &mut ctx.accounts.delegation;
68    let fee = &mut ctx.accounts.fee;
69    let registry = &ctx.accounts.registry;
70    let snapshot = &ctx.accounts.snapshot;
71    let snapshot_entry = &ctx.accounts.snapshot_entry;
72    let snapshot_frame = &ctx.accounts.snapshot_frame;
73    let thread = &ctx.accounts.thread;
74    let worker = &ctx.accounts.worker;
75
76    // Calculate the balance of this particular delegation, based on the weight of its stake with this worker.
77    let distribution_balance = if snapshot_frame.stake_amount > 0 {
78        fee.distributable_balance * snapshot_entry.stake_amount / snapshot_frame.stake_amount
79    } else {
80        0
81    };
82
83    // Transfer yield to the worker.
84    fee.sub_lamports(distribution_balance)?;
85    delegation.add_lamports(distribution_balance)?;
86
87    // Increment the delegation's yield balance.
88    delegation.yield_balance += distribution_balance;
89
90    // Build the next instruction for the thread.
91    let dynamic_instruction = if (snapshot_entry.id + 1) < snapshot_frame.total_entries {
92        // This frame has more entries. Move on to the next one.
93        let next_delegation_pubkey = Delegation::pubkey(worker.key(), delegation.id + 1);
94        let next_snapshot_entry_pubkey =
95            SnapshotEntry::pubkey(snapshot_frame.key(), snapshot_entry.id + 1);
96        Some(
97            Instruction {
98                program_id: crate::ID,
99                accounts: crate::accounts::DistributeFeesProcessEntry {
100                    config: config.key(),
101                    delegation: next_delegation_pubkey,
102                    fee: fee.key(),
103                    registry: registry.key(),
104                    snapshot: snapshot.key(),
105                    snapshot_entry: next_snapshot_entry_pubkey,
106                    snapshot_frame: snapshot_frame.key(),
107                    thread: thread.key(),
108                    worker: worker.key(),
109                }
110                .to_account_metas(Some(true)),
111                data: crate::instruction::DistributeFeesProcessEntry {}.data(),
112            }
113            .into(),
114        )
115    } else if (snapshot_frame.id + 1) < snapshot.total_frames {
116        // This frame has no more entries. Move on to the next worker.
117        let next_worker_pubkey = Worker::pubkey(worker.id + 1);
118        let next_snapshot_frame_pubkey =
119            SnapshotFrame::pubkey(snapshot.key(), snapshot_frame.id + 1);
120        Some(
121            Instruction {
122                program_id: crate::ID,
123                accounts: crate::accounts::DistributeFeesProcessFrame {
124                    config: config.key(),
125                    fee: Fee::pubkey(next_worker_pubkey),
126                    registry: registry.key(),
127                    snapshot: snapshot.key(),
128                    snapshot_frame: next_snapshot_frame_pubkey,
129                    thread: thread.key(),
130                    worker: next_worker_pubkey,
131                }
132                .to_account_metas(Some(true)),
133                data: crate::instruction::DistributeFeesProcessFrame {}.data(),
134            }
135            .into(),
136        )
137    } else {
138        None
139    };
140
141    Ok(ThreadResponse {
142        dynamic_instruction,
143        close_to: None,
144        trigger: None,
145    })
146}