Skip to content

Commit

Permalink
program: add fuel overflow account (#1449)
Browse files Browse the repository at this point in the history
* program: add fuel sweep account

* linter

* add reset_fuel_season admin ix, tests

* review comments

* add signer to sweep_fuel

* add missing signer

* program: fix precision issue with hlm liq fee (#1465)

* program: fix precision issue with hlm liq fee

* update test

* CHANGELOG

* program: allow hot wallet to init market inactive (#1454)

* program: allow hot wallet to init market inactive

* rm check for admin has one

* CHANGELOG

* program: change rounding for calc max withdrawable (#1461)

* program: avoid insufficient collateral from calculate_max_withdrawable_amount

* change test

* CHANGELOG

* CHANGELOG

---------

Co-authored-by: lil perp <[email protected]>
  • Loading branch information
wphan and crispheaney authored Feb 7, 2025
1 parent 56a638c commit 0eacedf
Show file tree
Hide file tree
Showing 16 changed files with 1,208 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- program: add pyth lazer stable coin oracle type ([#1463](https://github.com/drift-labs/protocol-v2/pull/1463))
- program: allow hot wallet admin to init market if not active ([#1454](https://github.com/drift-labs/protocol-v2/pull/1454))
- program: round down 1 for calculate_max_withdrawable ([#1461](https://github.com/drift-labs/protocol-v2/pull/1461))
- program: add fuel overflow account ([#1449](https://github.com/drift-labs/protocol-v2/pull/1449))

### Fixes

Expand Down
4 changes: 4 additions & 0 deletions programs/drift/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,10 @@ pub enum ErrorCode {
InvalidLiquidateSpotWithSwap,
#[msg("User in swift message does not match user in ix context")]
SwiftUserContextUserMismatch,
#[msg("User fuel overflow threshold not met")]
UserFuelOverflowThresholdNotMet,
#[msg("FuelOverflow account not found")]
FuelOverflowAccountNotFound,
}

#[macro_export]
Expand Down
188 changes: 185 additions & 3 deletions programs/drift/src/instructions/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::controller::spot_position::{
update_spot_balances_and_cumulative_deposits_with_limits,
};
use crate::error::ErrorCode;
use crate::ids::admin_hot_wallet;
use crate::ids::{
jupiter_mainnet_3, jupiter_mainnet_4, jupiter_mainnet_6, lighthouse, marinade_mainnet,
serum_program,
Expand All @@ -44,8 +45,8 @@ use crate::print_error;
use crate::safe_decrement;
use crate::safe_increment;
use crate::state::events::{
DepositDirection, DepositExplanation, DepositRecord, LPAction, LPRecord, NewUserRecord,
OrderActionExplanation, SwapRecord,
DepositDirection, DepositExplanation, DepositRecord, FuelSeasonRecord, FuelSweepRecord,
LPAction, LPRecord, NewUserRecord, OrderActionExplanation, SwapRecord,
};
use crate::state::fill_mode::FillMode;
use crate::state::fulfillment_params::drift::MatchFulfillmentParams;
Expand Down Expand Up @@ -77,7 +78,10 @@ use crate::state::swift_user::SwiftUserOrdersLoader;
use crate::state::swift_user::{SwiftUserOrders, SWIFT_PDA_SEED};
use crate::state::traits::Size;
use crate::state::user::ReferrerStatus;
use crate::state::user::{MarginMode, MarketType, OrderType, ReferrerName, User, UserStats};
use crate::state::user::{
FuelOverflow, FuelOverflowProvider, MarginMode, MarketType, OrderType, ReferrerName, User,
UserStats,
};
use crate::state::user_map::{load_user_maps, UserMap, UserStatsMap};
use crate::validate;
use crate::validation::sig_verification::verify_ed25519_ix;
Expand Down Expand Up @@ -318,6 +322,128 @@ pub fn handle_resize_swift_user_orders<'c: 'info, 'info>(
Ok(())
}

pub fn handle_initialize_fuel_overflow<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, InitializeFuelOverflow<'info>>,
) -> Result<()> {
let mut user_stats = load_mut!(&ctx.accounts.user_stats)?;
validate!(
user_stats.can_sweep_fuel(),
ErrorCode::UserFuelOverflowThresholdNotMet,
"User fuel sweep threshold not met"
)?;

let mut fuel_overflow = ctx
.accounts
.fuel_overflow
.load_init()
.or(Err(ErrorCode::UnableToLoadAccountLoader))?;

*fuel_overflow = FuelOverflow {
authority: ctx.accounts.authority.key(),
..FuelOverflow::default()
};
user_stats.update_fuel_overflow_status(true);

Ok(())
}

pub fn handle_sweep_fuel<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, SweepFuel<'info>>,
) -> anchor_lang::Result<()> {
let mut user_stats = load_mut!(&ctx.accounts.user_stats)?;
validate!(
user_stats.can_sweep_fuel(),
ErrorCode::UserFuelOverflowThresholdNotMet,
"User fuel sweep threshold not met"
)?;

let mut fuel_overflow = load_mut!(&ctx.accounts.fuel_overflow)?;

let clock = Clock::get()?;
emit!(FuelSweepRecord {
ts: clock.unix_timestamp.cast()?,
authority: ctx.accounts.authority.key(),
user_stats_fuel_insurance: user_stats.fuel_insurance,
user_stats_fuel_deposits: user_stats.fuel_deposits,
user_stats_fuel_borrows: user_stats.fuel_borrows,
user_stats_fuel_positions: user_stats.fuel_positions,
user_stats_fuel_taker: user_stats.fuel_taker,
user_stats_fuel_maker: user_stats.fuel_maker,
fuel_overflow_fuel_insurance: fuel_overflow.fuel_insurance,
fuel_overflow_fuel_deposits: fuel_overflow.fuel_deposits,
fuel_overflow_fuel_borrows: fuel_overflow.fuel_borrows,
fuel_overflow_fuel_positions: fuel_overflow.fuel_positions,
fuel_overflow_fuel_taker: fuel_overflow.fuel_taker,
fuel_overflow_fuel_maker: fuel_overflow.fuel_maker,
});

fuel_overflow.update_from_user_stats(&user_stats, clock.unix_timestamp.cast()?)?;
user_stats.reset_fuel();

Ok(())
}

pub fn handle_reset_fuel_season<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, ResetFuelSeason<'info>>,
) -> Result<()> {
let mut user_stats = load_mut!(&ctx.accounts.user_stats)?;

let fuel_overflow = ctx.fuel_overflow();
user_stats.validate_fuel_overflow(&fuel_overflow)?;

let clock = Clock::get()?;
if let Some(fuel_overflow_account) = fuel_overflow {
// if FuelOverflow exists, sweep before resetting user_stats
let mut fuel_overflow = load_mut!(fuel_overflow_account)?;
emit!(FuelSweepRecord {
ts: clock.unix_timestamp.cast()?,
authority: ctx.accounts.authority.key(),
user_stats_fuel_insurance: user_stats.fuel_insurance,
user_stats_fuel_deposits: user_stats.fuel_deposits,
user_stats_fuel_borrows: user_stats.fuel_borrows,
user_stats_fuel_positions: user_stats.fuel_positions,
user_stats_fuel_taker: user_stats.fuel_taker,
user_stats_fuel_maker: user_stats.fuel_maker,
fuel_overflow_fuel_insurance: fuel_overflow.fuel_insurance,
fuel_overflow_fuel_deposits: fuel_overflow.fuel_deposits,
fuel_overflow_fuel_borrows: fuel_overflow.fuel_borrows,
fuel_overflow_fuel_positions: fuel_overflow.fuel_positions,
fuel_overflow_fuel_taker: fuel_overflow.fuel_taker,
fuel_overflow_fuel_maker: fuel_overflow.fuel_maker,
});
fuel_overflow.update_from_user_stats(&user_stats, clock.unix_timestamp.cast()?)?;

emit!(FuelSeasonRecord {
ts: clock.unix_timestamp.cast()?,
authority: ctx.accounts.authority.key(),
fuel_insurance: fuel_overflow.fuel_insurance,
fuel_deposits: fuel_overflow.fuel_deposits,
fuel_borrows: fuel_overflow.fuel_borrows,
fuel_positions: fuel_overflow.fuel_positions,
fuel_taker: fuel_overflow.fuel_taker,
fuel_maker: fuel_overflow.fuel_maker,
fuel_total: fuel_overflow.total_fuel()?,
});
fuel_overflow.reset_fuel(clock.unix_timestamp.cast()?);
} else {
emit!(FuelSeasonRecord {
ts: clock.unix_timestamp.cast()?,
authority: ctx.accounts.authority.key(),
fuel_insurance: user_stats.fuel_insurance.cast()?,
fuel_deposits: user_stats.fuel_deposits.cast()?,
fuel_borrows: user_stats.fuel_borrows.cast()?,
fuel_positions: user_stats.fuel_positions.cast()?,
fuel_taker: user_stats.fuel_taker.cast()?,
fuel_maker: user_stats.fuel_maker.cast()?,
fuel_total: user_stats.total_fuel()?,
});
};

user_stats.reset_fuel();

Ok(())
}

#[access_control(
deposit_not_paused(&ctx.accounts.state)
)]
Expand Down Expand Up @@ -3225,6 +3351,62 @@ pub struct ResizeSwiftUserOrders<'info> {
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct InitializeFuelOverflow<'info> {
#[account(
init,
seeds = [b"fuel_overflow", authority.key.as_ref()],
space = FuelOverflow::SIZE,
bump,
payer = payer
)]
pub fuel_overflow: AccountLoader<'info, FuelOverflow>,
#[account(
mut,
has_one = authority
)]
pub user_stats: AccountLoader<'info, UserStats>,
/// CHECK: authority
pub authority: AccountInfo<'info>,
#[account(mut)]
pub payer: Signer<'info>,
pub rent: Sysvar<'info, Rent>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct SweepFuel<'info> {
#[account(
mut,
has_one = authority,
)]
pub fuel_overflow: AccountLoader<'info, FuelOverflow>,
#[account(
mut,
has_one = authority
)]
pub user_stats: AccountLoader<'info, UserStats>,
/// CHECK: authority
pub authority: AccountInfo<'info>,
pub signer: Signer<'info>,
}

#[derive(Accounts)]
pub struct ResetFuelSeason<'info> {
#[account(
mut,
has_one = authority
)]
pub user_stats: AccountLoader<'info, UserStats>,
/// CHECK: authority
pub authority: AccountInfo<'info>,
pub state: Box<Account<'info, State>>,
#[account(
constraint = admin.key() == admin_hot_wallet::id() || admin.key() == state.admin
)]
pub admin: Signer<'info>,
}

#[derive(Accounts)]
#[instruction(
name: [u8; 32],
Expand Down
18 changes: 18 additions & 0 deletions programs/drift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,24 @@ pub mod drift {
handle_resize_swift_user_orders(ctx, num_orders)
}

pub fn initialize_fuel_overflow<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, InitializeFuelOverflow<'info>>,
) -> Result<()> {
handle_initialize_fuel_overflow(ctx)
}

pub fn sweep_fuel<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, SweepFuel<'info>>,
) -> Result<()> {
handle_sweep_fuel(ctx)
}

pub fn reset_fuel_season<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, ResetFuelSeason<'info>>,
) -> Result<()> {
handle_reset_fuel_season(ctx)
}

pub fn initialize_referrer_name(
ctx: Context<InitializeReferrerName>,
name: [u8; 32],
Expand Down
1 change: 1 addition & 0 deletions programs/drift/src/math/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ pub const SPOT_MARKET_TOKEN_TWAP_WINDOW: i64 = TWENTY_FOUR_HOUR;
// FUEL
pub const FUEL_WINDOW_U128: u128 = EPOCH_DURATION as u128;
pub const FUEL_START_TS: i64 = 1723147200_i64; // August 8 2024 UTC
pub const FUEL_OVERFLOW_THRESHOLD_U32: u32 = 4_000_000_000; // slightly below u32 max to allow sweeping before limit is hit

// PREDICTION
pub const MAX_PREDICTION_MARKET_PRICE: u64 = PRICE_PRECISION_U64;
Expand Down
33 changes: 33 additions & 0 deletions programs/drift/src/state/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,39 @@ pub struct DeleteUserRecord {
pub keeper: Option<Pubkey>,
}

#[event]
pub struct FuelSweepRecord {
pub ts: i64,
pub authority: Pubkey,
// fuel values on UserStats before sweep
pub user_stats_fuel_insurance: u32,
pub user_stats_fuel_deposits: u32,
pub user_stats_fuel_borrows: u32,
pub user_stats_fuel_positions: u32,
pub user_stats_fuel_taker: u32,
pub user_stats_fuel_maker: u32,
// fuel values on FuelOverflow before sweep
pub fuel_overflow_fuel_insurance: u128,
pub fuel_overflow_fuel_deposits: u128,
pub fuel_overflow_fuel_borrows: u128,
pub fuel_overflow_fuel_positions: u128,
pub fuel_overflow_fuel_taker: u128,
pub fuel_overflow_fuel_maker: u128,
}

#[event]
pub struct FuelSeasonRecord {
pub ts: i64,
pub authority: Pubkey,
pub fuel_insurance: u128,
pub fuel_deposits: u128,
pub fuel_borrows: u128,
pub fuel_positions: u128,
pub fuel_taker: u128,
pub fuel_maker: u128,
pub fuel_total: u128,
}

pub fn emit_stack<T: AnchorSerialize + Discriminator, const N: usize>(event: T) -> DriftResult {
let mut data_buf = [0u8; N];
let mut out_buf = [0u8; N];
Expand Down
Loading

0 comments on commit 0eacedf

Please sign in to comment.