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_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    {
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        // Zero out the PDA data, required for changing the owner program
80        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
114/// Undelegate an account
115pub 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    // Account signer seeds
132    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    // Re-create the original PDA
139    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/// CPI to the delegation program to delegate the account
155#[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}