Skip to content

Commit 9570abf

Browse files
Fix/ephemeral balance undelegation 2.0 (#77)
* fix: fixed undelegation of ephemeral balance. Now undelegated to system program. Added new version of close_ephemeral_balance to support it. kept old one for backward-compatability * refactor: skip transfer if amount is 0 * refactor: addressed greptile comments * feat: undelegating escrow adding external_undelegate support to dlp * refactor: remove initial implementation * refactor: added docs + some refactor * Simplify escrow undelegation fix (#78) * fix: remove discriminator * chore: simplify fix * fix: delegation record with system program as owner & add tests --------- Co-authored-by: Gabriele Picco <[email protected]>
1 parent 4af7f1c commit 9570abf

9 files changed

+195
-67
lines changed

src/instruction_builder/close_ephemeral_balance.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use solana_program::instruction::Instruction;
2-
use solana_program::{instruction::AccountMeta, pubkey::Pubkey};
2+
use solana_program::{instruction::AccountMeta, pubkey::Pubkey, system_program};
33

44
use crate::discriminator::DlpDiscriminator;
55
use crate::pda::ephemeral_balance_pda_from_payer;
@@ -13,6 +13,7 @@ pub fn close_ephemeral_balance(payer: Pubkey, index: u8) -> Instruction {
1313
accounts: vec![
1414
AccountMeta::new(payer, true),
1515
AccountMeta::new(ephemeral_balance_pda, false),
16+
AccountMeta::new_readonly(system_program::id(), false),
1617
],
1718
data: [
1819
DlpDiscriminator::CloseEphemeralBalance.to_vec(),

src/instruction_builder/delegate_ephemeral_balance.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub fn delegate_ephemeral_balance(
2121
let delegated_account = ephemeral_balance_pda_from_payer(&pubkey, args.index);
2222
let delegate_buffer_pda = delegate_buffer_pda_from_delegated_account_and_owner_program(
2323
&delegated_account,
24-
&crate::id(),
24+
&system_program::id(),
2525
);
2626
let delegation_record_pda = delegation_record_pda_from_delegated_account(&delegated_account);
2727
let delegation_metadata_pda =

src/processor/close_ephemeral_balance.rs

+36-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
use crate::ephemeral_balance_seeds_from_payer;
2-
use crate::processor::utils::loaders::{load_initialized_pda, load_signer};
3-
use crate::processor::utils::pda::close_pda;
2+
use crate::processor::utils::loaders::{load_pda, load_signer};
3+
use solana_program::msg;
4+
use solana_program::program::invoke_signed;
45
use solana_program::program_error::ProgramError;
5-
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
6+
use solana_program::system_instruction::transfer;
7+
use solana_program::{
8+
account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey, system_program,
9+
};
610

711
/// Process the closing of an ephemeral balance account
812
///
913
/// Accounts:
1014
///
1115
/// 0: `[signer]` payer to pay for the transaction and receive the refund
1216
/// 1: `[writable]` ephemeral balance account we are closing
17+
/// 2: `[]` the system program
1318
///
1419
/// Requirements:
1520
///
@@ -27,21 +32,45 @@ pub fn process_close_ephemeral_balance(
2732
let index = *data.first().ok_or(ProgramError::InvalidInstructionData)?;
2833

2934
// Load Accounts
30-
let [payer, ephemeral_balance_account] = accounts else {
35+
let [payer, ephemeral_balance_account, system_program] = accounts else {
3136
return Err(ProgramError::NotEnoughAccountKeys);
3237
};
3338

3439
load_signer(payer, "payer")?;
3540

36-
load_initialized_pda(
41+
let ephemeral_balance_seeds: &[&[u8]] = ephemeral_balance_seeds_from_payer!(payer.key, index);
42+
let ephemeral_balance_bump = load_pda(
3743
ephemeral_balance_account,
38-
ephemeral_balance_seeds_from_payer!(payer.key, index),
44+
ephemeral_balance_seeds,
3945
&crate::id(),
4046
true,
4147
"ephemeral balance",
4248
)?;
49+
if ephemeral_balance_account.owner != &system_program::id() {
50+
msg!(
51+
"ephemeral balance expected to be owned by system program. got: {}",
52+
ephemeral_balance_account.owner
53+
);
54+
return Err(ProgramError::InvalidAccountOwner);
55+
}
4356

44-
close_pda(ephemeral_balance_account, payer)?;
57+
let amount = ephemeral_balance_account.lamports();
58+
if amount == 0 {
59+
return Ok(());
60+
}
61+
62+
let ephemeral_balance_bump_slice: &[u8] = &[ephemeral_balance_bump];
63+
let ephemeral_balance_signer_seeds =
64+
[ephemeral_balance_seeds, &[ephemeral_balance_bump_slice]].concat();
65+
invoke_signed(
66+
&transfer(ephemeral_balance_account.key, payer.key, amount),
67+
&[
68+
ephemeral_balance_account.clone(),
69+
payer.clone(),
70+
system_program.clone(),
71+
],
72+
&[&ephemeral_balance_signer_seeds],
73+
)?;
4574

4675
Ok(())
4776
}

src/processor/delegate.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,21 @@ pub fn process_delegate(
6363
load_owned_pda(delegated_account, &crate::id(), "delegated account")?;
6464
load_program(system_program, system_program::id(), "system program")?;
6565

66+
msg!("Delegating: {}", delegated_account.key);
67+
6668
// Validate seeds if the delegate account is not on curve, i.e. is a PDA
69+
// If the owner is the system program, we check if the account is derived from the delegation program,
70+
// allowing delegation of escrow accounts
6771
if !is_on_curve(delegated_account.key) {
6872
let seeds_to_validate: Vec<&[u8]> = args.seeds.iter().map(|v| v.as_slice()).collect();
73+
let program_id = if owner_program.key.eq(&system_program::id()) {
74+
crate::id()
75+
} else {
76+
*owner_program.key
77+
};
6978
let (derived_pda, _) =
70-
Pubkey::find_program_address(seeds_to_validate.as_ref(), owner_program.key);
79+
Pubkey::find_program_address(seeds_to_validate.as_ref(), &program_id);
80+
7181
if derived_pda.ne(delegated_account.key) {
7282
msg!(
7383
"Expected delegated PDA to be {}, but got {}",

src/processor/delegate_ephemeral_balance.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pub fn process_delegate_ephemeral_balance(
7676
let ix = crate::instruction_builder::delegate(
7777
*payer.key,
7878
*ephemeral_balance_account.key,
79-
Some(crate::id()),
79+
Some(system_program::id()),
8080
args.delegate_args,
8181
);
8282

src/processor/top_up_ephemeral_balance.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub fn process_top_up_ephemeral_balance(
5656
create_pda(
5757
ephemeral_balance_account,
5858
&system_program::id(),
59-
8,
59+
0,
6060
ephemeral_balance_seeds_from_payer!(pubkey.key, args.index),
6161
bump_ephemeral_balance,
6262
system_program,

src/state/utils/discriminator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub enum AccountDiscriminator {
1010
}
1111

1212
impl AccountDiscriminator {
13-
pub fn to_bytes(&self) -> [u8; 8] {
13+
pub const fn to_bytes(&self) -> [u8; 8] {
1414
let num = (*self) as u64;
1515
num.to_le_bytes()
1616
}

src/state/utils/to_bytes.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ macro_rules! impl_to_bytes_with_discriminator_borsh {
2323
impl $struct_name {
2424
pub fn to_bytes_with_discriminator<W: std::io::Write>(
2525
&self,
26-
data: &mut W,
26+
writer: &mut W,
2727
) -> Result<(), ::solana_program::program_error::ProgramError> {
28-
data.write_all(&Self::discriminator().to_bytes())?;
29-
self.serialize(data)?;
28+
writer.write_all(&Self::discriminator().to_bytes())?;
29+
self.serialize(writer)?;
3030
Ok(())
3131
}
3232
}

0 commit comments

Comments
 (0)