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, 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 let mut buffer_data = accounts.buffer.try_borrow_mut_data()?;
75 let new_data = accounts.pda.try_borrow_data()?.to_vec().clone();
76 (*buffer_data).copy_from_slice(&new_data);
77 drop(buffer_data);
78
79 close_pda(accounts.pda, accounts.payer)?;
81
82 create_pda(
84 accounts.pda,
85 accounts.delegation_program.key,
86 data_len,
87 pda_signer_seeds,
88 accounts.system_program,
89 accounts.payer,
90 )?;
91
92 let seeds_vec: Vec<Vec<u8>> = pda_seeds.iter().map(|&slice| slice.to_vec()).collect();
93
94 let delegation_args = DelegateAccountArgs {
95 commit_frequency_ms: config.commit_frequency_ms,
96 seeds: seeds_vec,
97 validator: config.validator,
98 };
99
100 cpi_delegate(
101 accounts.payer,
102 accounts.pda,
103 accounts.owner_program,
104 accounts.buffer,
105 accounts.delegation_record,
106 accounts.delegation_metadata,
107 accounts.system_program,
108 pda_signer_seeds,
109 delegation_args,
110 )?;
111
112 close_pda_with_system_transfer(
113 accounts.buffer,
114 buffer_signer_seeds,
115 accounts.payer,
116 accounts.system_program,
117 )?;
118 Ok(())
119}
120
121pub fn undelegate_account<'a, 'info>(
123 delegated_account: &'a AccountInfo<'info>,
124 owner_program: &Pubkey,
125 buffer: &'a AccountInfo<'info>,
126 payer: &'a AccountInfo<'info>,
127 system_program: &'a AccountInfo<'info>,
128 account_signer_seeds: Vec<Vec<u8>>,
129) -> ProgramResult {
130 if !buffer.is_signer {
131 return Err(ProgramError::MissingRequiredSignature);
132 }
133
134 let account_seeds: Vec<&[u8]> = account_signer_seeds.iter().map(|v| v.as_slice()).collect();
135
136 let (_, account_bump) = Pubkey::find_program_address(account_seeds.as_ref(), owner_program);
137
138 let account_bump_slice: &[u8] = &[account_bump];
140 let account_signer_seeds: &[&[&[u8]]] = &[&*seeds_with_bump(
141 account_seeds.as_ref(),
142 account_bump_slice,
143 )];
144
145 create_pda(
147 delegated_account,
148 owner_program,
149 buffer.data_len(),
150 account_signer_seeds,
151 system_program,
152 payer,
153 )?;
154
155 let mut data = delegated_account.try_borrow_mut_data()?;
156 let buffer_data = buffer.try_borrow_data()?;
157 (*data).copy_from_slice(&buffer_data);
158 Ok(())
159}
160
161#[allow(clippy::too_many_arguments)]
163pub fn cpi_delegate<'a, 'info>(
164 payer: &'a AccountInfo<'info>,
165 delegate_account: &'a AccountInfo<'info>,
166 owner_program: &'a AccountInfo<'info>,
167 buffer: &'a AccountInfo<'info>,
168 delegation_record: &'a AccountInfo<'info>,
169 delegation_metadata: &'a AccountInfo<'info>,
170 system_program: &'a AccountInfo<'info>,
171 signers_seeds: &[&[&[u8]]],
172 args: DelegateAccountArgs,
173) -> ProgramResult {
174 let mut data: Vec<u8> = vec![0u8; 8];
175 let serialized_seeds = args.try_to_vec()?;
176 data.extend_from_slice(&serialized_seeds);
177
178 let delegation_instruction = Instruction {
179 program_id: crate::id(),
180 accounts: vec![
181 AccountMeta::new(*payer.key, true),
182 AccountMeta::new(*delegate_account.key, true),
183 AccountMeta::new_readonly(*owner_program.key, false),
184 AccountMeta::new(*buffer.key, false),
185 AccountMeta::new(*delegation_record.key, false),
186 AccountMeta::new(*delegation_metadata.key, false),
187 AccountMeta::new_readonly(*system_program.key, false),
188 ],
189 data,
190 };
191
192 solana_program::program::invoke_signed(
193 &delegation_instruction,
194 &[
195 payer.clone(),
196 delegate_account.clone(),
197 owner_program.clone(),
198 buffer.clone(),
199 delegation_record.clone(),
200 delegation_metadata.clone(),
201 system_program.clone(),
202 ],
203 signers_seeds,
204 )
205}