|
| 1 | +use pinocchio::sysvars::rent::Rent; |
| 2 | +use pinocchio::sysvars::Sysvar; |
| 3 | +use pinocchio::{error::ProgramError, AccountView, Address, ProgramResult}; |
| 4 | + |
| 5 | +use crate::{ |
| 6 | + assert_owner, assert_signer, |
| 7 | + processor::{ |
| 8 | + initialize_rent_pda::RENT_PDA, internal::lamports_pda::derive_lamports_pda, |
| 9 | + }, |
| 10 | +}; |
| 11 | + |
| 12 | +const DLP_EPHEMERAL_BALANCE_TAG: &[u8] = b"balance"; |
| 13 | + |
| 14 | +#[inline(never)] |
| 15 | +pub fn process_close_lamports_pda_intent( |
| 16 | + accounts: &[AccountView], |
| 17 | + instruction_data: &[u8], |
| 18 | +) -> ProgramResult { |
| 19 | + let (escrow_index, salt) = parse_escrow_index_and_salt(instruction_data)?; |
| 20 | + if accounts.len() < 6 { |
| 21 | + return Err(ProgramError::NotEnoughAccountKeys); |
| 22 | + } |
| 23 | + |
| 24 | + let rent_pda_info = &accounts[0]; |
| 25 | + let lamports_pda_info = &accounts[1]; |
| 26 | + let payer_info = &accounts[2]; |
| 27 | + let destination_info = &accounts[3]; |
| 28 | + let escrow_authority = &accounts[accounts.len() - 2]; |
| 29 | + let escrow_signer = &accounts[accounts.len() - 1]; |
| 30 | + |
| 31 | + assert_signer!(escrow_signer); |
| 32 | + assert_owner!(rent_pda_info, &pinocchio_system::ID); |
| 33 | + assert_owner!(lamports_pda_info, &crate::ID); |
| 34 | + |
| 35 | + let escrow_index_seed = [escrow_index]; |
| 36 | + let (expected_escrow, _) = Address::find_program_address( |
| 37 | + &[ |
| 38 | + DLP_EPHEMERAL_BALANCE_TAG, |
| 39 | + escrow_authority.address().as_ref(), |
| 40 | + escrow_index_seed.as_ref(), |
| 41 | + ], |
| 42 | + &ephemeral_rollups_pinocchio::ID, |
| 43 | + ); |
| 44 | + if expected_escrow != *escrow_signer.address() { |
| 45 | + return Err(ProgramError::InvalidSeeds); |
| 46 | + } |
| 47 | + |
| 48 | + if RENT_PDA != *rent_pda_info.address() { |
| 49 | + return Err(ProgramError::InvalidSeeds); |
| 50 | + } |
| 51 | + if rent_pda_info.data_len() != 0 || lamports_pda_info.data_len() != 0 { |
| 52 | + return Err(ProgramError::InvalidAccountData); |
| 53 | + } |
| 54 | + |
| 55 | + let (derived_lamports_pda, _) = |
| 56 | + derive_lamports_pda(payer_info.address(), destination_info.address(), &salt); |
| 57 | + if derived_lamports_pda != *lamports_pda_info.address() { |
| 58 | + return Err(ProgramError::InvalidSeeds); |
| 59 | + } |
| 60 | + |
| 61 | + if lamports_pda_info.lamports() < Rent::get()?.try_minimum_balance(0)? { |
| 62 | + return Err(ProgramError::InvalidArgument); |
| 63 | + } |
| 64 | + |
| 65 | + close_program_account_to_recipient(lamports_pda_info, rent_pda_info) |
| 66 | +} |
| 67 | + |
| 68 | +fn parse_escrow_index_and_salt(instruction_data: &[u8]) -> Result<(u8, [u8; 32]), ProgramError> { |
| 69 | + if instruction_data.len() != 33 { |
| 70 | + return Err(ProgramError::InvalidInstructionData); |
| 71 | + } |
| 72 | + |
| 73 | + let mut salt = [0u8; 32]; |
| 74 | + salt.copy_from_slice(&instruction_data[1..]); |
| 75 | + Ok((instruction_data[0], salt)) |
| 76 | +} |
| 77 | + |
| 78 | +fn close_program_account_to_recipient( |
| 79 | + account: &AccountView, |
| 80 | + recipient: &AccountView, |
| 81 | +) -> ProgramResult { |
| 82 | + if *recipient.address() == *account.address() { |
| 83 | + return Err(ProgramError::InvalidArgument); |
| 84 | + } |
| 85 | + |
| 86 | + let lamports_to_refund = account.lamports(); |
| 87 | + let updated_recipient_lamports = recipient |
| 88 | + .lamports() |
| 89 | + .checked_add(lamports_to_refund) |
| 90 | + .ok_or(ProgramError::InvalidArgument)?; |
| 91 | + recipient.set_lamports(updated_recipient_lamports); |
| 92 | + account.set_lamports(0); |
| 93 | + account.close()?; |
| 94 | + Ok(()) |
| 95 | +} |
0 commit comments