ephemeral_rollups_sdk/
cpi.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
8// TODO: import from the delegation program crate once open-sourced
9use 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    // Pda signer seeds
53    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    // Buffer signer seeds
58    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 the Buffer PDA
64    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    // Copy the date to the buffer PDA
74    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 the PDA account
80    close_pda(accounts.pda, accounts.payer)?;
81
82    // Re-create the PDA setting the delegation program as owner
83    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
121/// Undelegate an account
122pub 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    // Account signer seeds
139    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    // Re-create the original PDA
146    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/// CPI to the delegation program to delegate the account
162#[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}