jito_priority_fee_distribution/
state.rs

1//! This module is where all PDA structs lives.
2
3use std::mem::size_of;
4
5use anchor_lang::prelude::*;
6
7use crate::ErrorCode::{AccountValidationFailure, ArithmeticError};
8
9#[account]
10#[derive(Default)]
11pub struct Config {
12    /// Account with authority over this PDA.
13    pub authority: Pubkey,
14
15    /// We want to expire funds after some time so that validators can be refunded the rent.
16    /// Expired funds will get transferred to this account.
17    pub expired_funds_account: Pubkey,
18
19    /// Specifies the number of epochs a merkle root is valid for before expiring.
20    pub num_epochs_valid: u64,
21
22    /// The maximum commission a validator can set on their distribution account.
23    pub max_validator_commission_bps: u16,
24
25    /// The epoch where lamports are transferred to the priority fee distribution account.
26    pub go_live_epoch: u64,
27
28    /// The bump used to generate this account
29    pub bump: u8,
30}
31
32/// The account that validators send priority fees to
33#[account]
34#[derive(Default)]
35pub struct PriorityFeeDistributionAccount {
36    /// The validator's vote account, also the recipient of remaining lamports after
37    /// upon closing this account.
38    pub validator_vote_account: Pubkey,
39
40    /// The only account authorized to upload a merkle-root for this account.
41    pub merkle_root_upload_authority: Pubkey,
42
43    /// The merkle root used to verify user claims from this account.
44    pub merkle_root: Option<MerkleRoot>,
45
46    /// Epoch for which this account was created.
47    pub epoch_created_at: u64,
48
49    /// The commission basis points this validator charges.
50    pub validator_commission_bps: u16,
51
52    /// The epoch (upto and including) that tip funds can be claimed.
53    pub expires_at: u64,
54
55    /// The total lamports transferred to this account.
56    pub total_lamports_transferred: u64,
57
58    /// The bump used to generate this account
59    pub bump: u8,
60}
61
62#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)]
63pub struct MerkleRoot {
64    /// The 256-bit merkle root.
65    pub root: [u8; 32],
66
67    /// Maximum number of funds that can ever be claimed from this [MerkleRoot].
68    pub max_total_claim: u64,
69
70    /// Maximum number of nodes that can ever be claimed from this [MerkleRoot].
71    pub max_num_nodes: u64,
72
73    /// Total funds that have been claimed.
74    pub total_funds_claimed: u64,
75
76    /// Number of nodes that have been claimed.
77    pub num_nodes_claimed: u64,
78}
79
80const HEADER_SIZE: usize = 8;
81
82impl Config {
83    pub const SEED: &'static [u8] = b"CONFIG_ACCOUNT";
84    pub const SIZE: usize = HEADER_SIZE + size_of::<Self>();
85
86    pub fn validate(&self) -> Result<()> {
87        const MAX_NUM_EPOCHS_VALID: u64 = 10;
88        const MAX_VALIDATOR_COMMISSION_BPS: u16 = 10000;
89
90        if self.num_epochs_valid == 0 || self.num_epochs_valid > MAX_NUM_EPOCHS_VALID {
91            return Err(AccountValidationFailure.into());
92        }
93
94        if self.max_validator_commission_bps > MAX_VALIDATOR_COMMISSION_BPS {
95            return Err(AccountValidationFailure.into());
96        }
97
98        let default_pubkey = Pubkey::default();
99        if self.expired_funds_account == default_pubkey || self.authority == default_pubkey {
100            return Err(AccountValidationFailure.into());
101        }
102
103        Ok(())
104    }
105}
106
107impl PriorityFeeDistributionAccount {
108    pub const SEED: &'static [u8] = b"PF_DISTRIBUTION_ACCOUNT";
109
110    pub const SIZE: usize = HEADER_SIZE + size_of::<Self>();
111
112    pub fn validate(&self) -> Result<()> {
113        let default_pubkey = Pubkey::default();
114        if self.validator_vote_account == default_pubkey
115            || self.merkle_root_upload_authority == default_pubkey
116        {
117            return Err(AccountValidationFailure.into());
118        }
119
120        Ok(())
121    }
122
123    pub fn claim_expired(from: AccountInfo, to: AccountInfo) -> Result<u64> {
124        let rent = Rent::get()?;
125        let min_rent_lamports = rent.minimum_balance(from.data_len());
126
127        let amount = from
128            .lamports()
129            .checked_sub(min_rent_lamports)
130            .ok_or(ArithmeticError)?;
131        Self::transfer_lamports(from, to, amount)?;
132
133        Ok(amount)
134    }
135
136    pub fn claim(from: AccountInfo, to: AccountInfo, amount: u64) -> Result<()> {
137        Self::transfer_lamports(from, to, amount)
138    }
139
140    pub fn increment_total_lamports_transferred(&mut self, amount: u64) -> Result<()> {
141        let old_balance = self.total_lamports_transferred;
142        let new_balance = old_balance.checked_add(amount).ok_or(ArithmeticError)?;
143
144        self.total_lamports_transferred = new_balance;
145
146        Ok(())
147    }
148
149    fn transfer_lamports(from: AccountInfo, to: AccountInfo, amount: u64) -> Result<()> {
150        // debit lamports
151        **from.try_borrow_mut_lamports()? =
152            from.lamports().checked_sub(amount).ok_or(ArithmeticError)?;
153        // credit lamports
154        **to.try_borrow_mut_lamports()? =
155            to.lamports().checked_add(amount).ok_or(ArithmeticError)?;
156
157        Ok(())
158    }
159}
160
161/// A PDA uniquely derived by the PriorityFeeDistributionAccount and claimant, which enforces an only-
162/// once claim by each claimant.
163/// @dev **this is very different than TipDistributor's ClaimStatus structure**
164#[account]
165#[derive(Default)]
166pub struct ClaimStatus {
167    /// The account that pays the rent for this account
168    pub claim_status_payer: Pubkey,
169
170    /// The epoch (upto and including) that tip funds can be claimed.
171    /// Copied since TDA can be closed, need to track to avoid making multiple claims
172    pub expires_at: u64,
173}
174
175impl ClaimStatus {
176    pub const SEED: &'static [u8] = b"CLAIM_STATUS";
177
178    pub const SIZE: usize = HEADER_SIZE + size_of::<Self>();
179}
180
181/// Singleton account that allows overriding TDA's merkle upload authority
182#[account]
183#[derive(Default)]
184pub struct MerkleRootUploadConfig {
185    /// The authority that overrides the PriorityFeeDistributionAccount merkle_root_upload_authority
186    pub override_authority: Pubkey,
187
188    /// The original merkle root upload authority that can be changed to the new overrided
189    /// authority. E.g. Jito Labs authority GZctHpWXmsZC1YHACTGGcHhYxjdRqQvTpYkb9LMvxDib
190    pub original_upload_authority: Pubkey,
191
192    /// The bump used to generate this account
193    pub bump: u8,
194}
195
196impl MerkleRootUploadConfig {
197    pub const SEED: &'static [u8] = b"ROOT_UPLOAD_CONFIG";
198
199    pub const SIZE: usize = HEADER_SIZE + size_of::<Self>();
200}