1use borsh::BorshSerialize;
2use solana_program::account_info::AccountInfo;
3use solana_program::entrypoint::ProgramResult;
4use solana_program::instruction::{AccountMeta, Instruction};
5use solana_program::program_error::ProgramError;
6use solana_program::pubkey::Pubkey;
7
8use crate::consts::BUFFER;
10use crate::types::DelegateAccountArgs;
11use crate::utils::{close_pda_with_system_transfer, create_pda, seeds_with_bump};
12
13pub struct DelegateAccounts<'a, 'info> {
14 pub payer: &'a AccountInfo<'info>,
15 pub pda: &'a AccountInfo<'info>,
16 pub owner_program: &'a AccountInfo<'info>,
17 pub buffer: &'a AccountInfo<'info>,
18 pub delegation_record: &'a AccountInfo<'info>,
19 pub delegation_metadata: &'a AccountInfo<'info>,
20 pub delegation_program: &'a AccountInfo<'info>,
21 pub system_program: &'a AccountInfo<'info>,
22}
23
24pub struct DelegateConfig {
25 pub commit_frequency_ms: u32,
26 pub validator: Option<Pubkey>,
27}
28
29impl Default for DelegateConfig {
30 fn default() -> Self {
31 DelegateConfig {
32 commit_frequency_ms: DelegateAccountArgs::default().commit_frequency_ms,
33 validator: DelegateAccountArgs::default().validator,
34 }
35 }
36}
37
38#[allow(clippy::needless_lifetimes)]
39pub fn delegate_account<'a, 'info>(
40 accounts: DelegateAccounts<'a, 'info>,
41 pda_seeds: &[&[u8]],
42 config: DelegateConfig,
43) -> ProgramResult {
44 let buffer_seeds: &[&[u8]] = &[BUFFER, accounts.pda.key.as_ref()];
45
46 let (_, delegate_account_bump) =
47 Pubkey::find_program_address(pda_seeds, accounts.owner_program.key);
48
49 let (_, buffer_pda_bump) =
50 Pubkey::find_program_address(buffer_seeds, accounts.owner_program.key);
51
52 let delegate_account_bump_slice: &[u8] = &[delegate_account_bump];
54 let pda_signer_seeds: &[&[&[u8]]] =
55 &[&*seeds_with_bump(pda_seeds, delegate_account_bump_slice)];
56
57 let buffer_bump_slice: &[u8] = &[buffer_pda_bump];
59 let buffer_signer_seeds: &[&[&[u8]]] = &[&*seeds_with_bump(buffer_seeds, buffer_bump_slice)];
60
61 let data_len = accounts.pda.data_len();
62
63 create_pda(
65 accounts.buffer,
66 accounts.owner_program.key,
67 data_len,
68 buffer_signer_seeds,
69 accounts.system_program,
70 accounts.payer,
71 )?;
72
73 {
75 let mut buffer_data = accounts.buffer.try_borrow_mut_data()?;
76 let mut pda_data = accounts.pda.try_borrow_mut_data()?;
77 (*buffer_data).copy_from_slice(&pda_data);
78
79 pda_data.fill(0);
81 }
82
83 accounts.pda.assign(accounts.delegation_program.key);
84
85 let seeds_vec: Vec<Vec<u8>> = pda_seeds.iter().map(|&slice| slice.to_vec()).collect();
86
87 let delegation_args = DelegateAccountArgs {
88 commit_frequency_ms: config.commit_frequency_ms,
89 seeds: seeds_vec,
90 validator: config.validator,
91 };
92
93 cpi_delegate(
94 accounts.payer,
95 accounts.pda,
96 accounts.owner_program,
97 accounts.buffer,
98 accounts.delegation_record,
99 accounts.delegation_metadata,
100 accounts.system_program,
101 pda_signer_seeds,
102 delegation_args,
103 )?;
104
105 close_pda_with_system_transfer(
106 accounts.buffer,
107 buffer_signer_seeds,
108 accounts.payer,
109 accounts.system_program,
110 )?;
111 Ok(())
112}
113
114pub fn undelegate_account<'a, 'info>(
116 delegated_account: &'a AccountInfo<'info>,
117 owner_program: &Pubkey,
118 buffer: &'a AccountInfo<'info>,
119 payer: &'a AccountInfo<'info>,
120 system_program: &'a AccountInfo<'info>,
121 account_signer_seeds: Vec<Vec<u8>>,
122) -> ProgramResult {
123 if !buffer.is_signer {
124 return Err(ProgramError::MissingRequiredSignature);
125 }
126
127 let account_seeds: Vec<&[u8]> = account_signer_seeds.iter().map(|v| v.as_slice()).collect();
128
129 let (_, account_bump) = Pubkey::find_program_address(account_seeds.as_ref(), owner_program);
130
131 let account_bump_slice: &[u8] = &[account_bump];
133 let account_signer_seeds: &[&[&[u8]]] = &[&*seeds_with_bump(
134 account_seeds.as_ref(),
135 account_bump_slice,
136 )];
137
138 create_pda(
140 delegated_account,
141 owner_program,
142 buffer.data_len(),
143 account_signer_seeds,
144 system_program,
145 payer,
146 )?;
147
148 let mut data = delegated_account.try_borrow_mut_data()?;
149 let buffer_data = buffer.try_borrow_data()?;
150 (*data).copy_from_slice(&buffer_data);
151 Ok(())
152}
153
154#[allow(clippy::too_many_arguments)]
156pub fn cpi_delegate<'a, 'info>(
157 payer: &'a AccountInfo<'info>,
158 delegate_account: &'a AccountInfo<'info>,
159 owner_program: &'a AccountInfo<'info>,
160 buffer: &'a AccountInfo<'info>,
161 delegation_record: &'a AccountInfo<'info>,
162 delegation_metadata: &'a AccountInfo<'info>,
163 system_program: &'a AccountInfo<'info>,
164 signers_seeds: &[&[&[u8]]],
165 args: DelegateAccountArgs,
166) -> ProgramResult {
167 let mut data: Vec<u8> = vec![0u8; 8];
168 args.serialize(&mut data)?;
169
170 let delegation_instruction = Instruction {
171 program_id: crate::id(),
172 accounts: vec![
173 AccountMeta::new(*payer.key, true),
174 AccountMeta::new(*delegate_account.key, true),
175 AccountMeta::new_readonly(*owner_program.key, false),
176 AccountMeta::new(*buffer.key, false),
177 AccountMeta::new(*delegation_record.key, false),
178 AccountMeta::new(*delegation_metadata.key, false),
179 AccountMeta::new_readonly(*system_program.key, false),
180 ],
181 data,
182 };
183
184 solana_program::program::invoke_signed(
185 &delegation_instruction,
186 &[
187 payer.clone(),
188 delegate_account.clone(),
189 owner_program.clone(),
190 buffer.clone(),
191 delegation_record.clone(),
192 delegation_metadata.clone(),
193 system_program.clone(),
194 ],
195 signers_seeds,
196 )
197}