1use std::fmt::Debug;
5
6use bytemuck::{Pod, Zeroable};
7use jito_bytemuck::{
8 types::{PodU16, PodU64},
9 AccountDeserialize, Discriminator,
10};
11use jito_restaking_sdk::error::RestakingError;
12use shank::ShankAccount;
13use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey};
14
15const RESERVED_SPACE_LEN: usize = 261;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Pod, Zeroable, AccountDeserialize, ShankAccount)]
20#[repr(C)]
21pub struct Operator {
22 pub base: Pubkey,
24
25 pub admin: Pubkey,
27
28 pub ncn_admin: Pubkey,
30
31 pub vault_admin: Pubkey,
33
34 pub delegate_admin: Pubkey,
36
37 pub metadata_admin: Pubkey,
39
40 pub voter: Pubkey,
43
44 index: PodU64,
46
47 ncn_count: PodU64,
50
51 vault_count: PodU64,
54
55 pub operator_fee_bps: PodU16,
57
58 pub bump: u8,
60
61 reserved_space: [u8; 261],
63}
64
65impl Operator {
66 pub fn new(base: Pubkey, admin: Pubkey, index: u64, operator_fee_bps: u16, bump: u8) -> Self {
73 Self {
74 base,
75 admin,
76 ncn_admin: admin,
77 vault_admin: admin,
78 delegate_admin: admin,
79 metadata_admin: admin,
80 voter: admin,
81 index: PodU64::from(index),
82 ncn_count: PodU64::from(0),
83 vault_count: PodU64::from(0),
84 operator_fee_bps: PodU16::from(operator_fee_bps),
85 bump,
86 reserved_space: [0; RESERVED_SPACE_LEN],
87 }
88 }
89
90 pub fn index(&self) -> u64 {
91 self.index.into()
92 }
93
94 pub fn ncn_count(&self) -> u64 {
95 self.ncn_count.into()
96 }
97
98 pub fn vault_count(&self) -> u64 {
99 self.vault_count.into()
100 }
101
102 pub fn increment_ncn_count(&mut self) -> Result<(), RestakingError> {
103 let mut ncn_count: u64 = self.ncn_count.into();
104 ncn_count = ncn_count
105 .checked_add(1)
106 .ok_or(RestakingError::NcnOverflow)?;
107 self.ncn_count = PodU64::from(ncn_count);
108 Ok(())
109 }
110
111 pub fn increment_vault_count(&mut self) -> Result<(), RestakingError> {
112 let mut vault_count: u64 = self.vault_count.into();
113 vault_count = vault_count
114 .checked_add(1)
115 .ok_or(RestakingError::VaultOverflow)?;
116 self.vault_count = PodU64::from(vault_count);
117 Ok(())
118 }
119
120 pub fn check_admin(&self, admin: &Pubkey) -> Result<(), RestakingError> {
133 if self.admin.ne(admin) {
134 msg!(
135 "Incorrect admin provided, expected {}, received {}",
136 self.admin,
137 admin
138 );
139 return Err(RestakingError::OperatorAdminInvalid);
140 }
141 Ok(())
142 }
143
144 pub fn check_delegate_admin(&self, delegate_admin: &Pubkey) -> Result<(), RestakingError> {
157 if self.delegate_admin.ne(delegate_admin) {
158 msg!(
159 "Incorrect delegate_admin provided, expected {}, received {}",
160 self.delegate_admin,
161 delegate_admin
162 );
163 return Err(RestakingError::OperatorDelegateAdminInvalid);
164 }
165 Ok(())
166 }
167
168 pub fn update_secondary_admin(&mut self, old_admin: &Pubkey, new_admin: &Pubkey) {
174 if self.ncn_admin.eq(old_admin) {
175 self.ncn_admin = *new_admin;
176 msg!("NCN admin set to {:?}", new_admin);
177 }
178
179 if self.vault_admin.eq(old_admin) {
180 self.vault_admin = *new_admin;
181 msg!("Vault admin set to {:?}", new_admin);
182 }
183
184 if self.voter.eq(old_admin) {
185 self.voter = *new_admin;
186 msg!("Voter set to {:?}", new_admin);
187 }
188
189 if self.delegate_admin.eq(old_admin) {
190 self.delegate_admin = *new_admin;
191 msg!("Delegate admin set to {:?}", new_admin);
192 }
193
194 if self.metadata_admin.eq(old_admin) {
195 self.metadata_admin = *new_admin;
196 msg!("Metadata admin set to {:?}", new_admin);
197 }
198 }
199
200 pub fn seeds(base: &Pubkey) -> Vec<Vec<u8>> {
208 Vec::from_iter([b"operator".to_vec(), base.as_ref().to_vec()])
209 }
210
211 pub fn find_program_address(program_id: &Pubkey, base: &Pubkey) -> (Pubkey, u8, Vec<Vec<u8>>) {
222 let seeds = Self::seeds(base);
223 let seeds_iter: Vec<_> = seeds.iter().map(|s| s.as_slice()).collect();
224 let (pda, bump) = Pubkey::find_program_address(&seeds_iter, program_id);
225 (pda, bump, seeds)
226 }
227
228 pub fn load(
238 program_id: &Pubkey,
239 account: &AccountInfo,
240 expect_writable: bool,
241 ) -> Result<(), ProgramError> {
242 if account.owner.ne(program_id) {
243 msg!("Operator account has an invalid owner");
244 return Err(ProgramError::InvalidAccountOwner);
245 }
246 if account.data_is_empty() {
247 msg!("Operator account data is empty");
248 return Err(ProgramError::InvalidAccountData);
249 }
250 if expect_writable && !account.is_writable {
251 msg!("Operator account is not writable");
252 return Err(ProgramError::InvalidAccountData);
253 }
254 if account.data.borrow()[0].ne(&Self::DISCRIMINATOR) {
255 msg!("Operator account discriminator is invalid");
256 return Err(ProgramError::InvalidAccountData);
257 }
258 let base = Self::try_from_slice_unchecked(&account.data.borrow())?.base;
259 if account
260 .key
261 .ne(&Self::find_program_address(program_id, &base).0)
262 {
263 msg!("Operator account is not at the correct PDA");
264 return Err(ProgramError::InvalidAccountData);
265 }
266 Ok(())
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use jito_bytemuck::types::{PodU16, PodU64};
273 use solana_program::pubkey::Pubkey;
274
275 use super::{Operator, RESERVED_SPACE_LEN};
276
277 #[test]
278 fn test_operator_no_padding() {
279 let operator_size = std::mem::size_of::<Operator>();
280 let sum_of_fields = std::mem::size_of::<Pubkey>() + std::mem::size_of::<Pubkey>() + std::mem::size_of::<Pubkey>() + std::mem::size_of::<Pubkey>() + std::mem::size_of::<Pubkey>() + std::mem::size_of::<Pubkey>() + std::mem::size_of::<Pubkey>() + std::mem::size_of::<PodU64>() + std::mem::size_of::<PodU64>() + std::mem::size_of::<PodU64>() + std::mem::size_of::<PodU16>() + std::mem::size_of::<u8>() + RESERVED_SPACE_LEN; assert_eq!(operator_size, sum_of_fields);
294 }
295
296 #[test]
297 fn test_update_secondary_admin_ok() {
298 let old_admin = Pubkey::new_unique();
299 let mut operator = Operator::new(Pubkey::new_unique(), old_admin, 0, 0, 0);
300
301 assert_eq!(operator.ncn_admin, old_admin);
302 assert_eq!(operator.vault_admin, old_admin);
303 assert_eq!(operator.voter, old_admin);
304 assert_eq!(operator.delegate_admin, old_admin);
305
306 let new_admin = Pubkey::new_unique();
307 operator.update_secondary_admin(&old_admin, &new_admin);
308
309 assert_eq!(operator.ncn_admin, new_admin);
310 assert_eq!(operator.vault_admin, new_admin);
311 assert_eq!(operator.voter, new_admin);
312 assert_eq!(operator.delegate_admin, new_admin);
313 assert_eq!(operator.metadata_admin, new_admin);
314 }
315}