delegation_program_sdk/
lib.rs

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
8pub use delegation_sdk_attribute_delegate::delegate;
9
10// TODO: import from the delegation program crate once open-sourced
11use crate::consts::BUFFER;
12use crate::types::DelegateAccountArgs;
13use crate::utils::{close_pda, create_pda, seeds_with_bump};
14pub mod consts;
15pub mod types;
16pub mod utils;
17
18pub fn id() -> Pubkey {
19    consts::DELEGATION_PROGRAM_ID
20}
21
22/// Delegate an account
23#[allow(clippy::too_many_arguments)]
24#[inline(always)]
25pub fn delegate_account<'a, 'info>(
26    payer: &'a AccountInfo<'info>,
27    pda: &'a AccountInfo<'info>,
28    owner_program: &'a AccountInfo<'info>,
29    buffer: &'a AccountInfo<'info>,
30    delegation_record: &'a AccountInfo<'info>,
31    delegation_metadata: &'a AccountInfo<'info>,
32    delegation_program: &'a AccountInfo<'info>,
33    system_program: &'a AccountInfo<'info>,
34    pda_seeds: &[&[u8]],
35    valid_until: i64,
36    commit_frequency_ms: u32,
37) -> ProgramResult {
38    let buffer_seeds: &[&[u8]] = &[BUFFER, pda.key.as_ref()];
39
40    let (_, delegate_account_bump) = Pubkey::find_program_address(pda_seeds, owner_program.key);
41
42    let (_, buffer_pda_bump) = Pubkey::find_program_address(buffer_seeds, owner_program.key);
43
44    // Pda signer seeds
45    let delegate_account_bump_slice: &[u8] = &[delegate_account_bump];
46    let pda_signer_seeds: &[&[&[u8]]] =
47        &[&*seeds_with_bump(pda_seeds, delegate_account_bump_slice)];
48
49    // Buffer signer seeds
50    let buffer_bump_slice: &[u8] = &[buffer_pda_bump];
51    let buffer_signer_seeds: &[&[&[u8]]] = &[&*seeds_with_bump(buffer_seeds, buffer_bump_slice)];
52
53    let data_len = pda.data_len();
54
55    // Create the Buffer PDA
56    create_pda(
57        buffer,
58        owner_program.key,
59        data_len,
60        buffer_signer_seeds,
61        system_program,
62        payer,
63    )?;
64
65    // Copy the date to the buffer PDA
66    let mut buffer_data = buffer.try_borrow_mut_data()?;
67    let new_data = pda.try_borrow_data()?.to_vec().clone();
68    (*buffer_data).copy_from_slice(&new_data);
69    drop(buffer_data);
70
71    // Close the PDA account
72    close_pda(pda, payer)?;
73
74    // Re-create the PDA setting the delegation program as owner
75    create_pda(
76        pda,
77        delegation_program.key,
78        data_len,
79        pda_signer_seeds,
80        system_program,
81        payer,
82    )?;
83
84    let seeds_vec: Vec<Vec<u8>> = pda_seeds.iter().map(|&slice| slice.to_vec()).collect();
85
86    let delegation_args = DelegateAccountArgs {
87        valid_until,
88        commit_frequency_ms,
89        seeds: seeds_vec,
90    };
91
92    cpi_delegate(
93        payer,
94        pda,
95        owner_program,
96        buffer,
97        delegation_record,
98        delegation_metadata,
99        system_program,
100        pda_signer_seeds,
101        delegation_args,
102    )?;
103
104    close_pda(buffer, payer)?;
105    Ok(())
106}
107
108/// Undelegate an account
109#[inline(always)]
110pub fn undelegate_account<'a, 'info>(
111    delegated_account: &'a AccountInfo<'info>,
112    owner_program: &Pubkey,
113    buffer: &'a AccountInfo<'info>,
114    payer: &'a AccountInfo<'info>,
115    system_program: &'a AccountInfo<'info>,
116    account_signer_seeds: Vec<Vec<u8>>,
117) -> ProgramResult {
118    if !buffer.is_signer {
119        return Err(ProgramError::MissingRequiredSignature);
120    }
121
122    let account_seeds: Vec<&[u8]> = account_signer_seeds.iter().map(|v| v.as_slice()).collect();
123
124    let (_, account_bump) = Pubkey::find_program_address(account_seeds.as_ref(), owner_program);
125
126    // Account signer seeds
127    let account_bump_slice: &[u8] = &[account_bump];
128    let account_signer_seeds: &[&[&[u8]]] = &[&*seeds_with_bump(
129        account_seeds.as_ref(),
130        account_bump_slice,
131    )];
132
133    // Re-create the original PDA
134    create_pda(
135        &delegated_account,
136        owner_program,
137        buffer.data_len(),
138        account_signer_seeds,
139        &system_program,
140        &payer,
141    )?;
142
143    let mut data = delegated_account.try_borrow_mut_data()?;
144    let buffer_data = buffer.try_borrow_data()?;
145    (*data).copy_from_slice(&buffer_data);
146    Ok(())
147}
148
149/// CPI to the delegation program to delegate the account
150#[allow(clippy::too_many_arguments)]
151#[inline(always)]
152pub fn cpi_delegate<'a, 'info>(
153    payer: &'a AccountInfo<'info>,
154    delegate_account: &'a AccountInfo<'info>,
155    owner_program: &'a AccountInfo<'info>,
156    buffer: &'a AccountInfo<'info>,
157    delegation_record: &'a AccountInfo<'info>,
158    delegation_metadata: &'a AccountInfo<'info>,
159    system_program: &'a AccountInfo<'info>,
160    signers_seeds: &[&[&[u8]]],
161    args: DelegateAccountArgs,
162) -> ProgramResult {
163    let mut data: Vec<u8> = vec![0u8; 8];
164    let serialized_seeds = args.try_to_vec()?;
165    data.extend_from_slice(&serialized_seeds);
166
167    let delegation_instruction = Instruction {
168        program_id: id(),
169        accounts: vec![
170            AccountMeta::new(*payer.key, true),
171            AccountMeta::new(*delegate_account.key, true),
172            AccountMeta::new_readonly(*owner_program.key, false),
173            AccountMeta::new(*buffer.key, false),
174            AccountMeta::new(*delegation_record.key, false),
175            AccountMeta::new(*delegation_metadata.key, false),
176            AccountMeta::new_readonly(*system_program.key, false),
177        ],
178        data,
179    };
180
181    solana_program::program::invoke_signed(
182        &delegation_instruction,
183        &[
184            payer.clone(),
185            delegate_account.clone(),
186            owner_program.clone(),
187            buffer.clone(),
188            delegation_record.clone(),
189            delegation_metadata.clone(),
190            system_program.clone(),
191        ],
192        signers_seeds,
193    )
194}
195
196/// CPI to the delegation program to allow undelegation
197#[inline(always)]
198pub fn allow_undelegation<'a, 'info>(
199    delegated_account: &'a AccountInfo<'info>,
200    delegation_record: &'a AccountInfo<'info>,
201    delegation_metedata: &'a AccountInfo<'info>,
202    buffer: &'a AccountInfo<'info>,
203    delegation_program: &'a AccountInfo<'info>,
204    owner_program: &Pubkey,
205) -> ProgramResult {
206    let buffer_seeds: &[&[u8]] = &[BUFFER, delegated_account.key.as_ref()];
207    let (_, buffer_pda_bump) = Pubkey::find_program_address(buffer_seeds, owner_program);
208    let buffer_bump_slice: &[u8] = &[buffer_pda_bump];
209    let buffer_signer_seeds: &[&[&[u8]]] = &[&*seeds_with_bump(buffer_seeds, buffer_bump_slice)];
210
211    let allow_undelegation_instruction = Instruction {
212        program_id: *delegation_program.key,
213        accounts: vec![
214            AccountMeta::new_readonly(*delegated_account.key, false),
215            AccountMeta::new_readonly(*delegation_record.key, false),
216            AccountMeta::new(*delegation_metedata.key, false),
217            AccountMeta::new_readonly(*buffer.key, true),
218        ],
219        data: vec![0x4, 0, 0, 0, 0, 0, 0, 0],
220    };
221
222    solana_program::program::invoke_signed(
223        &allow_undelegation_instruction,
224        &[
225            delegated_account.clone(),
226            delegation_record.clone(),
227            delegation_metedata.clone(),
228            buffer.clone(),
229        ],
230        buffer_signer_seeds,
231    )
232}
233
234/// CPI to trigger a commit of an account in the ER
235#[inline(always)]
236pub fn trigger_commit<'a, 'info>(
237    payer: &'a AccountInfo<'info>,
238    delegated_account: &'a AccountInfo<'info>,
239    magic_program: &'a AccountInfo<'info>,
240) -> ProgramResult {
241    let allow_undelegation_instruction = Instruction {
242        program_id: *magic_program.key,
243        accounts: vec![
244            AccountMeta::new_readonly(*payer.key, true),
245            AccountMeta::new_readonly(*delegated_account.key, false),
246        ],
247        data: vec![0x1, 0, 0, 0],
248    };
249
250    solana_program::program::invoke(
251        &allow_undelegation_instruction,
252        &[payer.clone(), delegated_account.clone()],
253    )
254}