Skip to content

Commit 290b7e9

Browse files
committed
merge master
2 parents 84ce292 + 7d63753 commit 290b7e9

31 files changed

+2022
-409
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313

1414
### Breaking
1515

16+
## [2.142.0] - 2025-10-14
17+
18+
### Features
19+
20+
- program: add titan to whitelisted swap programs ([#1952](https://github.com/drift-labs/protocol-v2/pull/1952))
21+
- program: allow hot wallet to increase max spread and pause funding ([#1957](https://github.com/drift-labs/protocol-v2/pull/1957))
22+
23+
### Fixes
24+
25+
### Breaking
26+
1627
## [2.141.0] - 2025-10-03
1728

1829
### Features

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

programs/drift/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "drift"
3-
version = "2.141.0"
3+
version = "2.142.0"
44
description = "Created with Anchor"
55
edition = "2018"
66

programs/drift/src/controller/amm.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,20 +185,33 @@ pub fn update_spreads(
185185
let max_ref_offset = market.amm.get_max_reference_price_offset()?;
186186

187187
let reference_price_offset = if max_ref_offset > 0 {
188-
let liquidity_ratio = amm_spread::calculate_inventory_liquidity_ratio(
189-
market.amm.base_asset_amount_with_amm,
190-
market.amm.base_asset_reserve,
191-
market.amm.min_base_asset_reserve,
192-
market.amm.max_base_asset_reserve,
193-
)?;
188+
let liquidity_ratio =
189+
amm_spread::calculate_inventory_liquidity_ratio_for_reference_price_offset(
190+
market.amm.base_asset_amount_with_amm,
191+
market.amm.base_asset_reserve,
192+
market.amm.min_base_asset_reserve,
193+
market.amm.max_base_asset_reserve,
194+
)?;
194195

195196
let signed_liquidity_ratio =
196197
liquidity_ratio.safe_mul(market.amm.get_protocol_owned_position()?.signum().cast()?)?;
197198

199+
let deadband_pct = market.amm.get_reference_price_offset_deadband_pct()?;
200+
let liquidity_fraction_after_deadband =
201+
if signed_liquidity_ratio.unsigned_abs() <= deadband_pct {
202+
0
203+
} else {
204+
signed_liquidity_ratio.safe_sub(
205+
deadband_pct
206+
.cast::<i128>()?
207+
.safe_mul(signed_liquidity_ratio.signum())?,
208+
)?
209+
};
210+
198211
amm_spread::calculate_reference_price_offset(
199212
reserve_price,
200213
market.amm.last_24h_avg_funding_rate,
201-
signed_liquidity_ratio,
214+
liquidity_fraction_after_deadband,
202215
market.amm.min_order_size,
203216
market
204217
.amm

programs/drift/src/controller/pnl.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,20 @@ pub fn settle_expired_position(
353353
) -> DriftResult {
354354
validate!(!user.is_bankrupt(), ErrorCode::UserBankrupt)?;
355355

356+
let position_index = match get_position_index(&user.perp_positions, perp_market_index) {
357+
Ok(index) => index,
358+
Err(_) => {
359+
msg!("User has no position for market {}", perp_market_index);
360+
return Ok(());
361+
}
362+
};
363+
364+
let can_skip_margin_calc = user.perp_positions[position_index].base_asset_amount == 0
365+
&& user.perp_positions[position_index].quote_asset_amount > 0;
366+
356367
// cannot settle pnl this way on a user who is in liquidation territory
357-
if !(meets_maintenance_margin_requirement(user, perp_market_map, spot_market_map, oracle_map)?)
368+
if !meets_maintenance_margin_requirement(user, perp_market_map, spot_market_map, oracle_map)?
369+
&& !can_skip_margin_calc
358370
{
359371
return Err(ErrorCode::InsufficientCollateralForSettlingPNL);
360372
}
@@ -390,14 +402,6 @@ pub fn settle_expired_position(
390402
None,
391403
)?;
392404

393-
let position_index = match get_position_index(&user.perp_positions, perp_market_index) {
394-
Ok(index) => index,
395-
Err(_) => {
396-
msg!("User has no position for market {}", perp_market_index);
397-
return Ok(());
398-
}
399-
};
400-
401405
let quote_spot_market = &mut spot_market_map.get_quote_spot_market_mut()?;
402406
let perp_market = &mut perp_market_map.get_ref_mut(&perp_market_index)?;
403407
validate!(

programs/drift/src/controller/position/tests.rs

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ fn amm_ref_price_offset_decay_logic() {
843843
.unwrap();
844844
assert_eq!(perp_market.amm.last_update_slot, clock_slot);
845845
assert_eq!(perp_market.amm.last_oracle_valid, true);
846-
assert_eq!(perp_market.amm.reference_price_offset, 7350);
846+
assert_eq!(perp_market.amm.reference_price_offset, 4458);
847847

848848
perp_market.amm.last_mark_price_twap_5min = (perp_market
849849
.amm
@@ -893,28 +893,28 @@ fn amm_ref_price_offset_decay_logic() {
893893
assert_eq!(
894894
offsets,
895895
[
896-
7140, 6930, 6720, 6510, 6300, 6090, 6070, 6050, 6030, 6010, 5800, 5590, 5380, 5170,
897-
4960, 4750, 4540, 4330, 4120, 3910, 3700, 3490, 3280, 3070, 2860, 2650, 2440, 2230,
898-
2020, 1810, 1620, 1449, 1296, 1158, 1034, 922, 821, 730, 648, 575, 509, 450, 396, 348,
899-
305, 266, 231, 199, 171, 145, 122, 101, 81, 61, 41, 21, 1, 0, 0, 0
896+
4248, 4038, 3828, 3618, 3408, 3198, 3178, 3158, 3138, 3118, 2908, 2698, 2488, 2278,
897+
2068, 1858, 1664, 1489, 1332, 1190, 1062, 947, 844, 751, 667, 592, 524, 463, 408, 359,
898+
315, 275, 239, 207, 178, 152, 128, 107, 87, 67, 47, 27, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0,
899+
0, 0, 0, 0, 0, 0, 0, 0
900900
]
901901
);
902902
assert_eq!(
903903
lspreads,
904904
[
905-
726, 726, 726, 726, 726, 726, 536, 536, 536, 536, 726, 726, 726, 726, 726, 726, 726,
906-
726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 706, 687, 669, 654,
907-
640, 628, 617, 607, 598, 589, 582, 575, 570, 564, 559, 555, 551, 548, 544, 542, 539,
908-
537, 536, 536, 536, 536, 536, 526, 526, 526
905+
726, 726, 726, 726, 726, 726, 536, 536, 536, 536, 726, 726, 726, 726, 726, 726, 710,
906+
691, 673, 658, 644, 631, 619, 609, 600, 591, 584, 577, 571, 565, 560, 556, 552, 548,
907+
545, 542, 540, 537, 536, 536, 536, 536, 536, 526, 526, 526, 526, 526, 526, 526, 526,
908+
526, 526, 526, 526, 526, 526, 526, 526, 526
909909
]
910910
);
911911
assert_eq!(
912912
sspreads,
913913
[
914-
7150, 6940, 6730, 6520, 6310, 6100, 6080, 6060, 6040, 6020, 5810, 5600, 5390, 5180,
915-
4970, 4760, 4550, 4340, 4130, 3920, 3710, 3500, 3290, 3080, 2870, 2660, 2450, 2240,
916-
2030, 1820, 1630, 1459, 1306, 1168, 1044, 932, 831, 740, 658, 585, 519, 460, 406, 358,
917-
315, 276, 241, 209, 181, 155, 132, 111, 91, 71, 51, 31, 11, 10, 10, 10
914+
4258, 4048, 3838, 3628, 3418, 3208, 3188, 3168, 3148, 3128, 2918, 2708, 2498, 2288,
915+
2078, 1868, 1674, 1499, 1342, 1200, 1072, 957, 854, 761, 677, 602, 534, 473, 418, 369,
916+
325, 285, 249, 217, 188, 162, 138, 117, 97, 77, 57, 37, 17, 10, 10, 10, 10, 10, 10, 10,
917+
10, 10, 10, 10, 10, 10, 10, 10, 10, 10
918918
]
919919
);
920920
}
@@ -1017,7 +1017,7 @@ fn amm_negative_ref_price_offset_decay_logic() {
10171017
.unwrap();
10181018
assert_eq!(perp_market.amm.last_update_slot, clock_slot);
10191019
assert_eq!(perp_market.amm.last_oracle_valid, true);
1020-
assert_eq!(perp_market.amm.reference_price_offset, 7350);
1020+
assert_eq!(perp_market.amm.reference_price_offset, 4458);
10211021

10221022
perp_market.amm.last_mark_price_twap_5min = (perp_market
10231023
.amm
@@ -1068,34 +1068,31 @@ fn amm_negative_ref_price_offset_decay_logic() {
10681068
assert_eq!(
10691069
offsets,
10701070
[
1071-
-7140, -6930, -6720, -6510, -6300, -6090, -6070, -6050, -6030, -6010, -5800, -5590,
1072-
-5380, -5170, -4960, -4750, -4540, -4330, -4120, -3910, -3700, -3490, -3280, -3070,
1073-
-2860, -2650, -2440, -2230, -2020, -1810, -1600, -1390, -1180, -970, -760, -550, -340,
1074-
-130, 0, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000,
1075-
10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000,
1076-
10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000,
1077-
10000, 10000, 10000, 10000, 10000, 10000
1071+
-4248, -4038, -3828, -3618, -3408, -3198, -3178, -3158, -3138, -3118, -2908, -2698,
1072+
-2488, -2278, -2068, -1858, -1648, -1438, -1228, -1018, -808, -598, -388, -178, 0,
1073+
7654, 7652, 7651, 7649, 7648, 7646, 7645, 7643, 7641, 7640, 7638, 7637, 7635, 7634,
1074+
7632, 7631, 7629, 7628, 7626, 7625, 7623, 7622, 7620, 7619, 7618, 7616, 7615, 7613,
1075+
7612, 7610, 7609, 7607, 7606, 7605, 7603, 7602, 7600, 7599, 7597, 7596, 7595, 7593,
1076+
7592, 7591, 7589, 7588, 7586, 7585, 7584, 7582, 7581, 7580, 7578, 7577, 7576
10781077
]
10791078
);
10801079
assert_eq!(
10811080
sspreads,
10821081
[
10831082
210, 210, 210, 210, 210, 210, 20, 20, 20, 20, 210, 210, 210, 210, 210, 210, 210, 210,
1084-
210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210, 210,
1085-
210, 210, 210, 130, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
1083+
210, 210, 210, 210, 210, 210, 178, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
10861084
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
1087-
10, 10
1085+
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
10881086
]
10891087
);
10901088
assert_eq!(
10911089
lspreads,
10921090
[
1093-
7666, 7456, 7246, 7036, 6826, 6616, 6596, 6576, 6556, 6536, 6326, 6116, 5906, 5696,
1094-
5486, 5276, 5066, 4856, 4646, 4436, 4226, 4016, 3806, 3596, 3386, 3176, 2966, 2756,
1095-
2546, 2336, 2126, 1916, 1706, 1496, 1286, 1076, 866, 656, 526, 526, 526, 526, 526, 526,
1091+
4774, 4564, 4354, 4144, 3934, 3724, 3704, 3684, 3664, 3644, 3434, 3224, 3014, 2804,
1092+
2594, 2384, 2174, 1964, 1754, 1544, 1334, 1124, 914, 704, 526, 526, 526, 526, 526, 526,
10961093
526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526,
10971094
526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526,
1098-
526, 526
1095+
526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526
10991096
]
11001097
);
11011098
}

programs/drift/src/ids.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,8 @@ pub mod dflow_mainnet_aggregator_4 {
120120
use solana_program::declare_id;
121121
declare_id!("DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH");
122122
}
123+
124+
pub mod titan_mainnet_argos_v1 {
125+
use solana_program::declare_id;
126+
declare_id!("T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT");
127+
}

programs/drift/src/instructions/admin.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,8 @@ pub fn handle_initialize_perp_market(
10961096
quote_asset_amount_with_unsettled_lp: 0,
10971097
reference_price_offset: 0,
10981098
amm_inventory_spread_adjustment: 0,
1099-
padding: [0; 3],
1099+
reference_price_offset_deadband_pct: 0,
1100+
padding: [0; 2],
11001101
last_funding_oracle_twap: 0,
11011102
},
11021103
};
@@ -3306,12 +3307,20 @@ pub fn handle_update_perp_market_status(
33063307
perp_market_valid(&ctx.accounts.perp_market)
33073308
)]
33083309
pub fn handle_update_perp_market_paused_operations(
3309-
ctx: Context<AdminUpdatePerpMarket>,
3310+
ctx: Context<HotAdminUpdatePerpMarket>,
33103311
paused_operations: u8,
33113312
) -> Result<()> {
33123313
let perp_market = &mut load_mut!(ctx.accounts.perp_market)?;
33133314
msg!("perp market {}", perp_market.market_index);
33143315

3316+
if *ctx.accounts.admin.key != ctx.accounts.state.admin {
3317+
validate!(
3318+
paused_operations == PerpOperation::UpdateFunding as u8,
3319+
ErrorCode::DefaultError,
3320+
"signer must be admin",
3321+
)?;
3322+
}
3323+
33153324
perp_market.paused_operations = paused_operations;
33163325

33173326
if perp_market.is_prediction_market() {
@@ -3487,6 +3496,49 @@ pub fn handle_update_perp_market_curve_update_intensity(
34873496
Ok(())
34883497
}
34893498

3499+
#[access_control(
3500+
perp_market_valid(&ctx.accounts.perp_market)
3501+
)]
3502+
pub fn handle_update_perp_market_reference_price_offset_deadband_pct(
3503+
ctx: Context<HotAdminUpdatePerpMarket>,
3504+
reference_price_offset_deadband_pct: u8,
3505+
) -> Result<()> {
3506+
validate!(
3507+
reference_price_offset_deadband_pct <= 100,
3508+
ErrorCode::DefaultError,
3509+
"invalid reference_price_offset_deadband_pct",
3510+
)?;
3511+
let perp_market = &mut load_mut!(ctx.accounts.perp_market)?;
3512+
msg!("perp market {}", perp_market.market_index);
3513+
3514+
msg!(
3515+
"perp_market.amm.reference_price_offset_deadband_pct: {} -> {}",
3516+
perp_market.amm.reference_price_offset_deadband_pct,
3517+
reference_price_offset_deadband_pct
3518+
);
3519+
3520+
let liquidity_ratio =
3521+
crate::math::amm_spread::calculate_inventory_liquidity_ratio_for_reference_price_offset(
3522+
perp_market.amm.base_asset_amount_with_amm,
3523+
perp_market.amm.base_asset_reserve,
3524+
perp_market.amm.min_base_asset_reserve,
3525+
perp_market.amm.max_base_asset_reserve,
3526+
)?;
3527+
3528+
let signed_liquidity_ratio = liquidity_ratio.safe_mul(
3529+
perp_market
3530+
.amm
3531+
.get_protocol_owned_position()?
3532+
.signum()
3533+
.cast()?,
3534+
)?;
3535+
3536+
msg!("current signed liquidity ratio: {}", signed_liquidity_ratio);
3537+
3538+
perp_market.amm.reference_price_offset_deadband_pct = reference_price_offset_deadband_pct;
3539+
Ok(())
3540+
}
3541+
34903542
pub fn handle_update_lp_cooldown_time(
34913543
ctx: Context<AdminUpdateState>,
34923544
lp_cooldown_time: u64,
@@ -3790,7 +3842,7 @@ pub fn handle_update_amm_jit_intensity(
37903842
perp_market_valid(&ctx.accounts.perp_market)
37913843
)]
37923844
pub fn handle_update_perp_market_max_spread(
3793-
ctx: Context<AdminUpdatePerpMarket>,
3845+
ctx: Context<HotAdminUpdatePerpMarket>,
37943846
max_spread: u32,
37953847
) -> Result<()> {
37963848
let perp_market = &mut load_mut!(ctx.accounts.perp_market)?;

programs/drift/src/instructions/keeper.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ use crate::controller::token::{receive, send_from_program_vault};
2424
use crate::error::ErrorCode;
2525
use crate::get_then_update_id;
2626
use crate::ids::admin_hot_wallet;
27-
use crate::ids::dflow_mainnet_aggregator_4;
28-
use crate::ids::{jupiter_mainnet_3, jupiter_mainnet_4, jupiter_mainnet_6, serum_program};
27+
use crate::ids::{
28+
dflow_mainnet_aggregator_4, jupiter_mainnet_3, jupiter_mainnet_4, jupiter_mainnet_6,
29+
serum_program, titan_mainnet_argos_v1,
30+
};
2931
use crate::instructions::constraints::*;
3032
use crate::instructions::optional_accounts::get_revenue_share_escrow_account;
3133
use crate::instructions::optional_accounts::{load_maps, AccountMaps};
@@ -1734,11 +1736,12 @@ pub fn handle_liquidate_spot_with_swap_begin<'c: 'info, 'info>(
17341736
jupiter_mainnet_4::ID,
17351737
jupiter_mainnet_6::ID,
17361738
dflow_mainnet_aggregator_4::ID,
1739+
titan_mainnet_argos_v1::ID,
17371740
];
17381741
validate!(
17391742
whitelisted_programs.contains(&ix.program_id),
17401743
ErrorCode::InvalidLiquidateSpotWithSwap,
1741-
"only allowed to pass in ixs to token, openbook, and Jupiter v3/v4/v6 programs"
1744+
"only allowed to pass in ixs to ATA, openbook, Jupiter v3/v4/v6, dflow, or titan programs"
17421745
)?;
17431746

17441747
for meta in ix.accounts.iter() {

programs/drift/src/instructions/user.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@ use crate::controller::spot_position::{
2222
};
2323
use crate::error::ErrorCode;
2424
use crate::ids::admin_hot_wallet;
25-
use crate::ids::dflow_mainnet_aggregator_4;
2625
use crate::ids::{
27-
jupiter_mainnet_3, jupiter_mainnet_4, jupiter_mainnet_6, lighthouse, marinade_mainnet,
28-
serum_program,
26+
dflow_mainnet_aggregator_4, jupiter_mainnet_3, jupiter_mainnet_4, jupiter_mainnet_6,
27+
lighthouse, marinade_mainnet, serum_program, titan_mainnet_argos_v1,
2928
};
3029
use crate::instructions::constraints::*;
3130
use crate::instructions::optional_accounts::get_revenue_share_escrow_account;
@@ -3686,6 +3685,7 @@ pub fn handle_begin_swap<'c: 'info, 'info>(
36863685
jupiter_mainnet_4::ID,
36873686
jupiter_mainnet_6::ID,
36883687
dflow_mainnet_aggregator_4::ID,
3688+
titan_mainnet_argos_v1::ID,
36893689
];
36903690
if !delegate_is_signer {
36913691
whitelisted_programs.push(Token::id());
@@ -3695,7 +3695,7 @@ pub fn handle_begin_swap<'c: 'info, 'info>(
36953695
validate!(
36963696
whitelisted_programs.contains(&ix.program_id),
36973697
ErrorCode::InvalidSwap,
3698-
"only allowed to pass in ixs to token, openbook, and Jupiter v3/v4/v6 programs"
3698+
"only allowed to pass in ixs to ATA, openbook, Jupiter v3/v4/v6, dflow, or titan programs"
36993699
)?;
37003700

37013701
for meta in ix.accounts.iter() {

0 commit comments

Comments
 (0)