Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
bf93ac1
feat: Impl RequestUndelegation
snawaz Jun 19, 2026
4d45bec
add CarryOverRequestedUndelegation
snawaz Jun 22, 2026
fc36480
address rabbit's feedback
snawaz Jun 24, 2026
723ead0
fix lint issues
snawaz Jun 24, 2026
4ac27be
address rabbit's feedback
snawaz Jun 24, 2026
3cd51cc
Use 30min as timeout for validator
snawaz Jun 24, 2026
2f367f4
add `df -h` to CI
snawaz Jun 24, 2026
7cdfee4
rename CarryOverRequestedUndelegation -> UndelegateAfterRequestTimeout
snawaz Jun 24, 2026
4c1531e
Use requires macros where possible
snawaz Jun 24, 2026
f87a7e5
replace parse_timeout_slots with RequestUndelegationArgs
snawaz Jun 24, 2026
607a737
rename UndelegateAfterRequestTimeout -> UndelegateWithRollbackAfterTi…
snawaz Jun 26, 2026
28bc3c1
revert unrelated changes regaring name changes
snawaz Jun 26, 2026
9449732
Remove request undelegation timeout args; timeout not configurable now
snawaz Jun 27, 2026
23fdbe8
remove wheels
snawaz Jun 27, 2026
7f6c793
Require owner-program authorization for rollback
snawaz Jun 27, 2026
fb47f7c
remove is_writable checks
snawaz Jun 27, 2026
ba823bb
rename is_undelegatable to undelegatable and replace bool with enum U…
snawaz Jun 27, 2026
0cca5a6
rename last_update_nonce -> last_commit_id
snawaz Jun 27, 2026
3f0768b
make request_undelegation truly idempotent
snawaz Jun 27, 2026
9301d09
rename undelegatable -> undelegation_requester
snawaz Jun 27, 2026
fb847a7
be compatible with borsh 0.10
snawaz Jun 28, 2026
ac0600f
increase timeout slots and remove stale test
snawaz Jun 28, 2026
ac3af3b
Allow UndelegationRequester::OwnerProgram to go through and fail iff …
snawaz Jun 28, 2026
db7ad48
remove else-block in process_request_undelegation()
snawaz Jul 1, 2026
6284135
use config-driven undelegation request setup in tests
snawaz Jul 2, 2026
212d344
require undelegation request accounts for owner-program requests in U…
snawaz Jul 6, 2026
074a1a4
ensure rent_payer is same as the delegation rent payer
snawaz Jul 6, 2026
2ab3a15
cleanup
snawaz Jul 7, 2026
26d01c4
Shrink undelegation request state, removing fields present in metadat…
snawaz Jul 7, 2026
a5cb190
Require allow_undelegation = true for owner-requested commits
snawaz Jul 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dlp-api/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub const COMMIT_FEE_LAMPORTS: u64 = 100_000;
/// Fixed fee per delegation session (0.0003 SOL).
pub const SESSION_FEE_LAMPORTS: u64 = 300_000;

/// Default and minimum timeout for requested undelegation.
pub const DEFAULT_UNDELEGATION_REQUEST_TIMEOUT_SLOTS: u64 = 300;

/// The discriminator for the external undelegate instruction.
pub const EXTERNAL_UNDELEGATE_DISCRIMINATOR: [u8; 8] =
[196, 28, 41, 206, 48, 37, 51, 167];
Expand Down
6 changes: 6 additions & 0 deletions dlp-api/src/discriminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ pub enum DlpDiscriminator {

/// See [crate::processor::process_delegate_magic_fee_vault] for docs.
DelegateMagicFeeVault = 25,

/// See [crate::processor::process_request_undelegation] for docs.
RequestUndelegation = 26,

/// See [crate::processor::process_carry_over_requested_undelegation] for docs.
CarryOverRequestedUndelegation = 27,
}

impl DlpDiscriminator {
Expand Down
27 changes: 27 additions & 0 deletions dlp-api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,33 @@ pub enum DlpError {
)]
InsufficientRent = 43,

#[error("Undelegation request PDA invalid seeds")]
UndelegationRequestInvalidSeeds = 44,

#[error("Undelegation request PDA invalid account owner")]
UndelegationRequestInvalidAccountOwner = 45,

#[error("Undelegation request PDA is already initialized")]
UndelegationRequestAlreadyInitialized = 46,

#[error("Undelegation request PDA immutable")]
UndelegationRequestImmutable = 47,

#[error("Invalid undelegation request")]
InvalidUndelegationRequest = 48,

#[error("Request undelegation is only supported for off-curve delegated accounts")]
RequestUndelegationOnCurveAccount = 49,

#[error("Undelegation request timeout is below the minimum")]
UndelegationRequestTimeoutTooShort = 50,

#[error("Undelegation request has not expired")]
UndelegationRequestNotExpired = 51,

#[error("Invalid pending commit state for timeout carry-over")]
InvalidPendingCommitState = 52,

#[error("An infallible error is encountered possibly due to logic error")]
InfallibleError = 100,
}
Expand Down
100 changes: 100 additions & 0 deletions dlp-api/src/instruction_builder/carry_over_requested_undelegation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use dlp::{
discriminator::DlpDiscriminator,
pda::{
commit_record_pda_from_delegated_account,
commit_state_pda_from_delegated_account,
delegation_metadata_pda_from_delegated_account,
delegation_record_pda_from_delegated_account,
undelegate_buffer_pda_from_delegated_account,
undelegation_request_pda_from_delegated_account,
},
total_size_budget, AccountSizeClass, DLP_PROGRAM_DATA_SIZE_CLASS,
};
use solana_program::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
};
use solana_sdk_ids::system_program;

use crate::compat::{Compatize, Modernize};

/// Builds a timeout carry-over instruction for a requested undelegation.
/// See [dlp::processor::process_carry_over_requested_undelegation] for docs.
#[allow(clippy::too_many_arguments)]
pub fn carry_over_requested_undelegation(
caller: Pubkey,
delegated_account: Pubkey,
owner_program: Pubkey,
request_rent_payer: Pubkey,
delegation_rent_payer: Pubkey,
commit_reimbursement: Pubkey,
) -> Instruction {
let delegated_account_compat = delegated_account.compatize();
let undelegate_buffer_pda =
undelegate_buffer_pda_from_delegated_account(&delegated_account_compat)
.modernize();
let request_pda = undelegation_request_pda_from_delegated_account(
&delegated_account_compat,
)
.modernize();
let delegation_record_pda =
delegation_record_pda_from_delegated_account(&delegated_account_compat)
.modernize();
let delegation_metadata_pda =
delegation_metadata_pda_from_delegated_account(
&delegated_account_compat,
)
.modernize();
let commit_state_pda =
commit_state_pda_from_delegated_account(&delegated_account_compat)
.modernize();
let commit_record_pda =
commit_record_pda_from_delegated_account(&delegated_account_compat)
.modernize();

Instruction {
program_id: dlp::id().modernize(),
accounts: vec![
AccountMeta::new(caller, true),
AccountMeta::new(delegated_account, false),
AccountMeta::new_readonly(owner_program, false),
AccountMeta::new(undelegate_buffer_pda, false),
AccountMeta::new(request_pda, false),
AccountMeta::new(delegation_record_pda, false),
AccountMeta::new(delegation_metadata_pda, false),
AccountMeta::new(request_rent_payer, false),
AccountMeta::new(delegation_rent_payer, false),
AccountMeta::new(commit_state_pda, false),
AccountMeta::new(commit_record_pda, false),
AccountMeta::new(commit_reimbursement, false),
AccountMeta::new_readonly(system_program::id(), false),
],
data: DlpDiscriminator::CarryOverRequestedUndelegation.to_vec(),
}
}

///
/// Returns accounts-data-size budget for requested undelegation carry-over.
///
/// This value can be used with ComputeBudgetInstruction::SetLoadedAccountsDataSizeLimit
///
pub fn carry_over_requested_undelegation_size_budget(
delegated_account: AccountSizeClass,
) -> u32 {
total_size_budget(&[
DLP_PROGRAM_DATA_SIZE_CLASS,
AccountSizeClass::Tiny, // caller
delegated_account, // delegated_account
AccountSizeClass::Tiny, // owner_program
delegated_account, // undelegate_buffer_pda
AccountSizeClass::Tiny, // undelegation_request_pda
AccountSizeClass::Tiny, // delegation_record_pda
AccountSizeClass::Tiny, // delegation_metadata_pda
AccountSizeClass::Tiny, // request_rent_payer
AccountSizeClass::Tiny, // delegation_rent_payer
delegated_account, // commit_state_pda
AccountSizeClass::Tiny, // commit_record_pda
AccountSizeClass::Tiny, // commit_reimbursement
AccountSizeClass::Tiny, // system_program
])
}
4 changes: 4 additions & 0 deletions dlp-api/src/instruction_builder/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod call_handler;
mod call_handler_v2;
mod carry_over_requested_undelegation;
mod close_ephemeral_balance;
mod close_validator_fees_vault;
mod commit_diff;
Expand All @@ -18,6 +19,7 @@ mod init_magic_fee_vault;
mod init_protocol_fees_vault;
mod init_validator_fees_vault;
mod protocol_claim_fees;
mod request_undelegation;
mod top_up_ephemeral_balance;
mod types;
mod undelegate;
Expand All @@ -27,6 +29,7 @@ mod whitelist_validator_for_program;

pub use call_handler::*;
pub use call_handler_v2::*;
pub use carry_over_requested_undelegation::*;
pub use close_ephemeral_balance::*;
pub use close_validator_fees_vault::*;
pub use commit_diff::*;
Expand All @@ -45,6 +48,7 @@ pub use init_magic_fee_vault::*;
pub use init_protocol_fees_vault::*;
pub use init_validator_fees_vault::*;
pub use protocol_claim_fees::*;
pub use request_undelegation::*;
pub use top_up_ephemeral_balance::*;
pub use types::*;
pub use undelegate::*;
Expand Down
102 changes: 102 additions & 0 deletions dlp-api/src/instruction_builder/request_undelegation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use dlp::{
discriminator::DlpDiscriminator,
pda::{
delegation_metadata_pda_from_delegated_account,
delegation_record_pda_from_delegated_account,
undelegation_request_pda_from_delegated_account,
},
total_size_budget, AccountSizeClass, DLP_PROGRAM_DATA_SIZE_CLASS,
};
use solana_program::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
};
use solana_sdk_ids::system_program;

use crate::compat::{Compatize, Modernize};

/// Builds a request undelegation instruction.
/// See [dlp::processor::process_request_undelegation] for docs.
pub fn request_undelegation(
payer: Pubkey,
delegated_account: Pubkey,
owner_program: Pubkey,
) -> Instruction {
build_request_undelegation(payer, delegated_account, owner_program, None)
}

/// Builds a request undelegation instruction with a custom timeout.
/// See [dlp::processor::process_request_undelegation] for docs.
pub fn request_undelegation_with_timeout(
payer: Pubkey,
delegated_account: Pubkey,
owner_program: Pubkey,
timeout_slots: u64,
) -> Instruction {
build_request_undelegation(
payer,
delegated_account,
owner_program,
Some(timeout_slots),
)
}

fn build_request_undelegation(
payer: Pubkey,
delegated_account: Pubkey,
owner_program: Pubkey,
timeout_slots: Option<u64>,
) -> Instruction {
let delegated_account_compat = delegated_account.compatize();
let undelegation_request_pda =
undelegation_request_pda_from_delegated_account(
&delegated_account_compat,
)
.modernize();
let delegation_record_pda =
delegation_record_pda_from_delegated_account(&delegated_account_compat)
.modernize();
let delegation_metadata_pda =
delegation_metadata_pda_from_delegated_account(
&delegated_account_compat,
)
.modernize();
let mut data = DlpDiscriminator::RequestUndelegation.to_vec();
if let Some(timeout_slots) = timeout_slots {
data.extend_from_slice(&timeout_slots.to_le_bytes());
}

Instruction {
program_id: dlp::id().modernize(),
accounts: vec![
AccountMeta::new(payer, true),
AccountMeta::new_readonly(delegated_account, true),
AccountMeta::new_readonly(owner_program, false),
AccountMeta::new(undelegation_request_pda, false),
AccountMeta::new_readonly(delegation_record_pda, false),
AccountMeta::new_readonly(delegation_metadata_pda, false),
AccountMeta::new_readonly(system_program::id(), false),
],
data,
}
}

///
/// Returns accounts-data-size budget for request undelegation instruction.
///
/// This value can be used with ComputeBudgetInstruction::SetLoadedAccountsDataSizeLimit
///
pub fn request_undelegation_size_budget(
delegated_account: AccountSizeClass,
) -> u32 {
total_size_budget(&[
DLP_PROGRAM_DATA_SIZE_CLASS,
AccountSizeClass::Tiny, // payer
delegated_account, // delegated_account
AccountSizeClass::Tiny, // owner_program
AccountSizeClass::Tiny, // undelegation_request_pda
AccountSizeClass::Tiny, // delegation_record_pda
AccountSizeClass::Tiny, // delegation_metadata_pda
AccountSizeClass::Tiny, // system_program
])
}
Loading
Loading