From 64ab59a195666351f47066d42028736205a8e35c Mon Sep 17 00:00:00 2001 From: bigz_Pubkey <83473873+0xbigz@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:51:18 -0400 Subject: [PATCH] sdk: add fuel-if-test (#1144) * sdk: add fuel-if-test * add FUEL_START_TS logic to last_fuel_if_bonus_update_ts --- programs/drift/src/controller/insurance.rs | 10 +- programs/drift/src/instructions/admin.rs | 20 ++-- programs/drift/src/math/amm.rs | 3 - sdk/src/types.ts | 2 +- sdk/src/user.ts | 19 ++- tests/fuel.ts | 133 ++++++++++++++++++++- 6 files changed, 166 insertions(+), 21 deletions(-) diff --git a/programs/drift/src/controller/insurance.rs b/programs/drift/src/controller/insurance.rs index f7c9686aa..9aedd5916 100644 --- a/programs/drift/src/controller/insurance.rs +++ b/programs/drift/src/controller/insurance.rs @@ -31,7 +31,7 @@ use crate::state::perp_market::PerpMarket; use crate::state::spot_market::{SpotBalanceType, SpotMarket}; use crate::state::state::State; use crate::state::user::UserStats; -use crate::{emit, validate, GOV_SPOT_MARKET_INDEX, QUOTE_SPOT_MARKET_INDEX}; +use crate::{emit, validate, FUEL_START_TS, GOV_SPOT_MARKET_INDEX, QUOTE_SPOT_MARKET_INDEX}; #[cfg(test)] mod tests; @@ -71,9 +71,13 @@ pub fn update_user_stats_if_stake_amount( user_stats.if_staked_gov_token_amount = if_stake_amount; } - if spot_market.fuel_boost_insurance != 0 { + if spot_market.fuel_boost_insurance != 0 && now >= FUEL_START_TS { let now_u32: u32 = now.cast()?; - let since_last = now_u32.safe_sub(user_stats.last_fuel_if_bonus_update_ts)?; + let since_last = now_u32.safe_sub( + user_stats + .last_fuel_if_bonus_update_ts + .max(FUEL_START_TS.cast()?), + )?; // calculate their stake amount prior to update let fuel_bonus_insurance = if_stake_amount diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index b46181654..b79d64e2e 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -3531,57 +3531,57 @@ pub fn handle_update_spot_market_fuel( if let Some(fuel_boost_taker) = fuel_boost_taker { msg!( - "perp_market.fuel_boost_taker: {:?} -> {:?}", + "spot_market.fuel_boost_taker: {:?} -> {:?}", spot_market.fuel_boost_taker, fuel_boost_taker ); spot_market.fuel_boost_taker = fuel_boost_taker; } else { - msg!("perp_market.fuel_boost_taker: unchanged"); + msg!("spot_market.fuel_boost_taker: unchanged"); } if let Some(fuel_boost_maker) = fuel_boost_maker { msg!( - "perp_market.fuel_boost_maker: {:?} -> {:?}", + "spot_market.fuel_boost_maker: {:?} -> {:?}", spot_market.fuel_boost_maker, fuel_boost_maker ); spot_market.fuel_boost_maker = fuel_boost_maker; } else { - msg!("perp_market.fuel_boost_maker: unchanged"); + msg!("spot_market.fuel_boost_maker: unchanged"); } if let Some(fuel_boost_deposits) = fuel_boost_deposits { msg!( - "perp_market.fuel_boost_deposits: {:?} -> {:?}", + "spot_market.fuel_boost_deposits: {:?} -> {:?}", spot_market.fuel_boost_deposits, fuel_boost_deposits ); spot_market.fuel_boost_deposits = fuel_boost_deposits; } else { - msg!("perp_market.fuel_boost_deposits: unchanged"); + msg!("spot_market.fuel_boost_deposits: unchanged"); } if let Some(fuel_boost_borrows) = fuel_boost_borrows { msg!( - "perp_market.fuel_boost_borrows: {:?} -> {:?}", + "spot_market.fuel_boost_borrows: {:?} -> {:?}", spot_market.fuel_boost_borrows, fuel_boost_borrows ); spot_market.fuel_boost_borrows = fuel_boost_borrows; } else { - msg!("perp_market.fuel_boost_borrows: unchanged"); + msg!("spot_market.fuel_boost_borrows: unchanged"); } if let Some(fuel_boost_insurance) = fuel_boost_insurance { msg!( - "perp_market.fuel_boost_insurance: {:?} -> {:?}", + "spot_market.fuel_boost_insurance: {:?} -> {:?}", spot_market.fuel_boost_insurance, fuel_boost_insurance ); spot_market.fuel_boost_insurance = fuel_boost_insurance; } else { - msg!("perp_market.fuel_boost_insurance: unchanged"); + msg!("spot_market.fuel_boost_insurance: unchanged"); } Ok(()) diff --git a/programs/drift/src/math/amm.rs b/programs/drift/src/math/amm.rs index dd9d731c3..68bdd169c 100644 --- a/programs/drift/src/math/amm.rs +++ b/programs/drift/src/math/amm.rs @@ -848,9 +848,6 @@ pub fn calculate_expiry_price( return Ok(target_price); } - // use crate::dlog!(amm - // .quote_asset_amount, pnl_pool_amount, amm.base_asset_amount_with_amm); - // net_baa * price + net_quote <= 0 // net_quote/net_baa <= -price diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 08ef678f7..1b0a71e43 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -887,7 +887,7 @@ export type UserStatsAccount = { authority: PublicKey; ifStakedQuoteAssetAmount: BN; - lastFuelBonusUpdateTs: number; // u32 onchain + lastFuelIfBonusUpdateTs: number; // u32 onchain fuelInsurance: number; fuelDeposits: number; diff --git a/sdk/src/user.ts b/sdk/src/user.ts index e4fb7589d..ce71e6742 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -1011,7 +1011,7 @@ export class User { this.driftClient.getSpotMarketAccount(GOV_SPOT_MARKET_INDEX); const fuelBonusNumeratorUserStats = now.sub( - new BN(userStats.lastFuelBonusUpdateTs) + BN.max(new BN(userStats.lastFuelIfBonusUpdateTs), FUEL_START_TS) ); result.insuranceFuel = result.insuranceFuel.add( @@ -1022,6 +1022,23 @@ export class User { ) ); } + + if (userStats.ifStakedQuoteAssetAmount.gt(ZERO)) { + const spotMarketAccount: SpotMarketAccount = + this.driftClient.getSpotMarketAccount(QUOTE_SPOT_MARKET_INDEX); + + const fuelBonusNumeratorUserStats = now.sub( + BN.max(new BN(userStats.lastFuelIfBonusUpdateTs), FUEL_START_TS) + ); + + result.insuranceFuel = result.insuranceFuel.add( + calculateInsuranceFuelBonus( + spotMarketAccount, + userStats.ifStakedQuoteAssetAmount, + fuelBonusNumeratorUserStats + ) + ); + } } return result; diff --git a/tests/fuel.ts b/tests/fuel.ts index 208286719..39cb14ae5 100644 --- a/tests/fuel.ts +++ b/tests/fuel.ts @@ -13,6 +13,7 @@ import { User, Wallet, BASE_PRECISION, + UserStatsAccount, getLimitOrderParams, OracleSource, ONE, @@ -34,7 +35,7 @@ import { startAnchor } from 'solana-bankrun'; import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; -describe('place and fill spot order', () => { +describe("fuelin'", () => { const chProgram = anchor.workspace.Drift as Program; let fillerDriftClient: TestClient; @@ -63,7 +64,7 @@ describe('place and fill spot order', () => { const wallet = new Wallet(keypair); const userUSDCAccount = await mockUserUSDCAccount( usdcMint, - usdcAmount, + usdcAmount.muln(100), bankrunContextWrapper, keypair.publicKey ); @@ -115,7 +116,7 @@ describe('place and fill spot order', () => { usdcMint = await mockUSDCMint(bankrunContextWrapper); userUSDCAccount = await mockUserUSDCAccount( usdcMint, - usdcAmount, + usdcAmount.muln(10), bankrunContextWrapper ); @@ -211,6 +212,132 @@ describe('place and fill spot order', () => { await fillerDriftClientUser.unsubscribe(); }); + it('fuel for staking', async () => { + const [takerDriftClient, _takerUSDCAccount] = await createTestClient({ + referrer: fillerDriftClientUser.getUserAccount().authority, + referrerStats: fillerDriftClient.getUserStatsAccountPublicKey(), + }); + const takerDriftClientUser = new User({ + driftClient: takerDriftClient, + userAccountPublicKey: await takerDriftClient.getUserAccountPublicKey(), + accountSubscription: { + type: 'polling', + accountLoader: bulkAccountLoader, + }, + }); + await takerDriftClientUser.subscribe(); + + const marketIndex = 0; + + const txSig = await takerDriftClient.addInsuranceFundStake({ + marketIndex: marketIndex, + amount: usdcAmount, + collateralAccountPublicKey: _takerUSDCAccount.publicKey, + initializeStakeAccount: true, + }); + bankrunContextWrapper.connection.printTxLogs(txSig); + await takerDriftClient.fetchAccounts(); + + const takerUserStats: UserStatsAccount = takerDriftClient + .getUserStats() + .getAccount(); + + console.log(takerUserStats); + assert(takerUserStats.ifStakedQuoteAssetAmount.gt(ZERO)); + + await fillerDriftClient.updateSpotMarketFuel(0, 100, 200, 200, 0, 250); + const currentClockInit = + await bankrunContextWrapper.context.banksClient.getClock(); + + await takerDriftClient.fetchAccounts(); + assert(takerDriftClient.getSpotMarketAccount(0).fuelBoostInsurance > 0); + + const fuelDictInit = takerDriftClientUser.getFuelBonus( + new BN(currentClockInit.unixTimestamp.toString()).addn(36000), + true, + true + ); + + console.log(fuelDictInit); + assert(fuelDictInit['insuranceFuel'].gt(ZERO)); + + const timeProgress = 36000; // 30 days in seconds + + await bankrunContextWrapper.moveTimeForward(timeProgress); + + const _ = await takerDriftClient.requestRemoveInsuranceFundStake( + marketIndex, + usdcAmount.divn(3) + ); + + const currentClockInit2 = + await bankrunContextWrapper.context.banksClient.getClock(); + + const fuelDictInit2 = takerDriftClientUser.getFuelBonus( + new BN(currentClockInit2.unixTimestamp.toString()), + true, + true + ); + + console.log(fuelDictInit2); + assert(fuelDictInit2['insuranceFuel'].gt(ZERO)); + console.log( + 'insurance before/after:', + fuelDictInit['insuranceFuel'].toNumber(), + '->', + fuelDictInit2['insuranceFuel'].toNumber() + ); + + assert( + Math.abs( + fuelDictInit['insuranceFuel'].toNumber() - + fuelDictInit2['insuranceFuel'].toNumber() + ) <= 1 + ); + + await bankrunContextWrapper.moveTimeForward( + takerDriftClient + .getSpotMarketAccount(0) + .insuranceFund.unstakingPeriod.toNumber() + ); + + const _again = await takerDriftClient.removeInsuranceFundStake( + marketIndex, + _takerUSDCAccount.publicKey + ); + + const currentClockRm = + await bankrunContextWrapper.context.banksClient.getClock(); + + const fuelDictRmStake = takerDriftClientUser.getFuelBonus( + new BN(currentClockRm.unixTimestamp.toString()), + true, + true + ); + + console.log(fuelDictRmStake); + const totalFuelIfNoUnstake = 119791; + const expectedFuel = + (totalFuelIfNoUnstake - fuelDictInit2['insuranceFuel'].toNumber()) / 3 + + fuelDictInit2['insuranceFuel'].toNumber(); + console.log( + expectedFuel, + 'vs', + fuelDictRmStake['insuranceFuel'].toNumber() + ); + assert( + Math.abs(fuelDictRmStake['insuranceFuel'].toNumber() - expectedFuel) < 1 + ); + + assert(fuelDictRmStake['insuranceFuel'].gt(ZERO)); + console.log( + 'insurance before/after remove:', + fuelDictInit2['insuranceFuel'].toNumber(), + '->', + fuelDictRmStake['insuranceFuel'].toNumber() + ); + }); + it('fuel for perp taker/maker/position', async () => { const [takerDriftClient, _takerUSDCAccount] = await createTestClient({ referrer: fillerDriftClientUser.getUserAccount().authority,