ephemeral_rollups_sdk/
cpi.rs

1use crate::types::DelegateAccountArgs;
2use crate::utils::{close_pda_with_system_transfer, create_pda, seeds_with_bump};
3use borsh::BorshSerialize;
4use dlp::delegate_buffer_seeds_from_delegated_account;
5
6use crate::solana_compat::solana::{
7    invoke_signed, sol_memset, system_instruction, AccountInfo, AccountMeta, Instruction,
8    ProgramError, ProgramResult, Pubkey,
9};
10
11pub const DELEGATION_PROGRAM_ID: Pubkey =
12    Pubkey::new_from_array(dlp::consts::DELEGATION_PROGRAM_ID.to_bytes());
13
14pub struct DelegateAccounts<'a, 'info> {
15    pub payer: &'a AccountInfo<'info>,
16    pub pda: &'a AccountInfo<'info>,
17    pub owner_program: &'a AccountInfo<'info>,
18    pub buffer: &'a AccountInfo<'info>,
19    pub delegation_record: &'a AccountInfo<'info>,
20    pub delegation_metadata: &'a AccountInfo<'info>,
21    pub delegation_program: &'a AccountInfo<'info>,
22    pub system_program: &'a AccountInfo<'info>,
23}
24
25pub struct DelegateConfig {
26    pub commit_frequency_ms: u32,
27    pub validator: Option<Pubkey>,
28}
29
30impl Default for DelegateConfig {
31    fn default() -> Self {
32        DelegateConfig {
33            commit_frequency_ms: DelegateAccountArgs::default().commit_frequency_ms,
34            validator: DelegateAccountArgs::default().validator,
35        }
36    }
37}
38
39#[allow(clippy::needless_lifetimes)]
40pub fn delegate_account<'a, 'info>(
41    accounts: DelegateAccounts<'a, 'info>,
42    pda_seeds: &[&[u8]],
43    config: DelegateConfig,
44) -> ProgramResult {
45    let buffer_seeds: &[&[u8]] = delegate_buffer_seeds_from_delegated_account!(accounts.pda.key);
46
47    let (_, delegate_account_bump) =
48        Pubkey::find_program_address(pda_seeds, accounts.owner_program.key);
49
50    let (_, buffer_pda_bump) =
51        Pubkey::find_program_address(buffer_seeds, accounts.owner_program.key);
52
53    // Pda signer seeds
54    let delegate_account_bump_slice: &[u8] = &[delegate_account_bump];
55    let pda_signer_seeds: &[&[&[u8]]] =
56        &[&*seeds_with_bump(pda_seeds, delegate_account_bump_slice)];
57
58    // Buffer signer seeds
59    let buffer_bump_slice: &[u8] = &[buffer_pda_bump];
60    let buffer_signer_seeds: &[&[&[u8]]] = &[&*seeds_with_bump(buffer_seeds, buffer_bump_slice)];
61
62    let data_len = accounts.pda.data_len();
63
64    // Create the Buffer PDA
65    create_pda(
66        accounts.buffer,
67        accounts.owner_program.key,
68        data_len,
69        buffer_signer_seeds,
70        accounts.system_program,
71        accounts.payer,
72        false,
73    )?;
74
75    // Copy PDA -> buffer (RO pda, RW buffer)
76    {
77        let pda_ro = accounts.pda.try_borrow_data()?;
78        let mut buf = accounts.buffer.try_borrow_mut_data()?;
79        buf.copy_from_slice(&pda_ro);
80    }
81
82    // Zero PDA (single RW borrow)
83    {
84        let mut pda_mut = accounts.pda.try_borrow_mut_data()?;
85        #[allow(unused_unsafe)]
86        unsafe {
87            sol_memset(&mut pda_mut, 0, data_len)
88        };
89    }
90
91    // Assign the PDA to the delegation program if not already assigned
92    if accounts.pda.owner != accounts.system_program.key {
93        accounts.pda.assign(accounts.system_program.key);
94    }
95    if accounts.pda.owner != accounts.delegation_program.key {
96        invoke_signed(
97            &system_instruction::assign(accounts.pda.key, accounts.delegation_program.key),
98            &[accounts.pda.clone(), accounts.system_program.clone()],
99            pda_signer_seeds,
100        )?;
101    }
102
103    let seeds_vec: Vec<Vec<u8>> = pda_seeds.iter().map(|&slice| slice.to_vec()).collect();
104
105    let delegation_args = DelegateAccountArgs {
106        commit_frequency_ms: config.commit_frequency_ms,
107        seeds: seeds_vec,
108        validator: config.validator,
109    };
110
111    cpi_delegate(
112        accounts.payer,
113        accounts.pda,
114        accounts.owner_program,
115        accounts.buffer,
116        accounts.delegation_record,
117        accounts.delegation_metadata,
118        accounts.system_program,
119        pda_signer_seeds,
120        delegation_args,
121    )?;
122
123    close_pda_with_system_transfer(
124        accounts.buffer,
125        buffer_signer_seeds,
126        accounts.payer,
127        accounts.system_program,
128    )?;
129    Ok(())
130}
131
132/// Undelegate an account
133pub fn undelegate_account<'a, 'info>(
134    delegated_account: &'a AccountInfo<'info>,
135    owner_program: &Pubkey,
136    buffer: &'a AccountInfo<'info>,
137    payer: &'a AccountInfo<'info>,
138    system_program: &'a AccountInfo<'info>,
139    account_signer_seeds: Vec<Vec<u8>>,
140) -> ProgramResult {
141    if !buffer.is_signer {
142        return Err(ProgramError::MissingRequiredSignature);
143    }
144    if buffer.owner != &DELEGATION_PROGRAM_ID {
145        return Err(ProgramError::InvalidAccountOwner);
146    }
147
148    let account_seeds: Vec<&[u8]> = account_signer_seeds.iter().map(|v| v.as_slice()).collect();
149
150    let (_, account_bump) = Pubkey::find_program_address(account_seeds.as_ref(), owner_program);
151
152    // Account signer seeds
153    let account_bump_slice: &[u8] = &[account_bump];
154    let account_signer_seeds: &[&[&[u8]]] = &[&*seeds_with_bump(
155        account_seeds.as_ref(),
156        account_bump_slice,
157    )];
158
159    // Re-create the original PDA
160    create_pda(
161        delegated_account,
162        owner_program,
163        buffer.data_len(),
164        account_signer_seeds,
165        system_program,
166        payer,
167        true,
168    )?;
169
170    let mut data = delegated_account.try_borrow_mut_data()?;
171    let buffer_data = buffer.try_borrow_data()?;
172    (*data).copy_from_slice(&buffer_data);
173    Ok(())
174}
175
176/// CPI to the delegation program to delegate the account
177#[allow(clippy::too_many_arguments)]
178pub fn cpi_delegate<'a, 'info>(
179    payer: &'a AccountInfo<'info>,
180    delegate_account: &'a AccountInfo<'info>,
181    owner_program: &'a AccountInfo<'info>,
182    buffer: &'a AccountInfo<'info>,
183    delegation_record: &'a AccountInfo<'info>,
184    delegation_metadata: &'a AccountInfo<'info>,
185    system_program: &'a AccountInfo<'info>,
186    signers_seeds: &[&[&[u8]]],
187    args: DelegateAccountArgs,
188) -> ProgramResult {
189    let mut data: Vec<u8> = vec![0u8; 8];
190    args.serialize(&mut data)?;
191
192    let delegation_instruction = Instruction {
193        program_id: crate::id().to_bytes().into(),
194        accounts: vec![
195            AccountMeta::new(payer.key.to_bytes().into(), true),
196            AccountMeta::new(delegate_account.key.to_bytes().into(), true),
197            AccountMeta::new_readonly(owner_program.key.to_bytes().into(), false),
198            AccountMeta::new(buffer.key.to_bytes().into(), false),
199            AccountMeta::new(delegation_record.key.to_bytes().into(), false),
200            AccountMeta::new(delegation_metadata.key.to_bytes().into(), false),
201            AccountMeta::new_readonly(system_program.key.to_bytes().into(), false),
202        ],
203        data,
204    };
205
206    invoke_signed(
207        &delegation_instruction,
208        &[
209            payer.clone(),
210            delegate_account.clone(),
211            owner_program.clone(),
212            buffer.clone(),
213            delegation_record.clone(),
214            delegation_metadata.clone(),
215            system_program.clone(),
216        ],
217        signers_seeds,
218    )
219}