From 1b64e52a60e2bac6270fe123d5b86377afd50835 Mon Sep 17 00:00:00 2001 From: soundsonacid Date: Thu, 25 Jul 2024 12:39:30 -0500 Subject: [PATCH] add fuel --- protocol-v2 | 2 +- src/driftpy/constants/numeric_constants.py | 5 + src/driftpy/drift_user.py | 109 +++++++++++++++++++++ src/driftpy/idl/drift.json | 7 +- src/driftpy/math/fuel.py | 56 +++++++++++ src/driftpy/types.py | 22 ++--- update_idl.sh | 1 + 7 files changed, 185 insertions(+), 17 deletions(-) create mode 100644 src/driftpy/math/fuel.py diff --git a/protocol-v2 b/protocol-v2 index a8ff7ee4..d7fa4068 160000 --- a/protocol-v2 +++ b/protocol-v2 @@ -1 +1 @@ -Subproject commit a8ff7ee4af6b961328d656cfa3dd4e6c5e56425b +Subproject commit d7fa4068456289299fcfa24e87a14f7456f1d814 diff --git a/src/driftpy/constants/numeric_constants.py b/src/driftpy/constants/numeric_constants.py index 92529455..6eccd01b 100644 --- a/src/driftpy/constants/numeric_constants.py +++ b/src/driftpy/constants/numeric_constants.py @@ -130,3 +130,8 @@ OPEN_ORDER_MARGIN_REQUIREMENT = QUOTE_PRECISION / 100 LIQUIDATION_PCT_PRECISION = TEN_THOUSAND + +FUEL_START_TS = 1722384000 +FUEL_WINDOW = 60 * 60 * 24 * 28 + +GOV_SPOT_MARKET_INDEX = 15 diff --git a/src/driftpy/drift_user.py b/src/driftpy/drift_user.py index d8ea5e73..ac3c0ce6 100644 --- a/src/driftpy/drift_user.py +++ b/src/driftpy/drift_user.py @@ -12,6 +12,11 @@ from driftpy.math.margin import * from driftpy.math.spot_balance import get_strict_token_value from driftpy.math.spot_market import * +from driftpy.math.fuel import ( + calculate_spot_fuel_bonus, + calculate_perp_fuel_bonus, + calculate_insurance_fuel_bonus, +) from driftpy.accounts.oracle import * from driftpy.math.spot_position import ( get_worst_case_token_amounts, @@ -1290,3 +1295,107 @@ def get_net_spot_market_value( ) return total_asset_value - total_liability_value + + def get_fuel_bonus( + self, now: int, include_settled: bool = True, include_unsettled: bool = True + ) -> dict[str, int]: + user_account = self.get_user_account() + total_fuel = { + "insurance_fuel": 0, + "taker_fuel": 0, + "maker_fuel": 0, + "deposit_fuel": 0, + "borrow_fuel": 0, + "position_fuel": 0, + } + + if include_settled: + user_stats = self.drift_client.get_user_stats().get_account() + total_fuel["taker_fuel"] += user_stats.fuel_taker + total_fuel["maker_fuel"] += user_stats.fuel_maker + total_fuel["deposit_fuel"] += user_stats.fuel_deposits + total_fuel["borrow_fuel"] += user_stats.fuel_borrows + total_fuel["position_fuel"] += user_stats.fuel_positions + + if include_unsettled: + # fuel bonus numerator is the time since the last fuel bonus update, capped at the start of the fuel program + fuel_bonus_numerator = max( + now - max(user_account.last_fuel_bonus_update_ts, FUEL_START_TS), 0 + ) + if fuel_bonus_numerator > 0: + for spot_position in self.get_active_spot_positions(): + spot_market_account = self.drift_client.get_spot_market_account( + spot_position.market_index + ) + token_amount = self.get_token_amount(spot_position.market_index) + oracle_price_data = self.get_oracle_data_for_spot_market( + spot_position.market_index + ) + twap_5min = calculate_live_oracle_twap( + spot_market_account.historical_oracle_data, + oracle_price_data, + now, + FIVE_MINUTE, + ) + strict_oracle_price = StrictOraclePrice( + oracle_price_data.price, twap_5min + ) + signed_token_value = get_strict_token_value( + token_amount, spot_market_account.decimals, strict_oracle_price + ) + spot_fuel = calculate_spot_fuel_bonus( + spot_market_account, signed_token_value, fuel_bonus_numerator + ) + if signed_token_value > 0: + total_fuel["deposit_fuel"] += spot_fuel + else: + total_fuel["borrow_fuel"] += spot_fuel + + for perp_position in self.get_active_perp_positions(): + oracle_price_data = self.get_oracle_data_for_perp_market( + perp_position.market_index + ) + perp_market_account = self.drift_client.get_perp_market_account( + perp_position.market_index + ) + base_asset_value = self.get_perp_position_value( + perp_position.market_index, oracle_price_data, False + ) + total_fuel["position_fuel"] += calculate_perp_fuel_bonus( + perp_market_account, base_asset_value, fuel_bonus_numerator + ) + + user_stats = self.drift_client.get_user_stats().get_account() + + if user_stats.if_staked_gov_token_amount > 0: + spot_market_account = self.drift_client.get_spot_market_account( + GOV_SPOT_MARKET_INDEX + ) + fuel_bonus_numerator_user_stats = ( + now - user_stats.last_fuel_bonus_update_ts + ) + total_fuel["insurance_fuel"] += calculate_insurance_fuel_bonus( + spot_market_account, + user_stats.if_staked_gov_token_amount, + fuel_bonus_numerator_user_stats, + ) + + return total_fuel + + def get_perp_position_value( + self, + market_index: int, + oracle_price_data: OraclePriceData, + include_open_orders: bool = False, + ): + perp_position = self.get_perp_position_with_lp_settle(market_index)[ + 0 + ] or self.get_empty_position(market_index) + + market = self.drift_client.get_perp_market_account(perp_position.market_index) + + perp_position_value = calculate_base_asset_value_with_oracle( + market, perp_position, oracle_price_data, include_open_orders + ) + + return perp_position_value diff --git a/src/driftpy/idl/drift.json b/src/driftpy/idl/drift.json index 6839e90c..b9c1b64b 100644 --- a/src/driftpy/idl/drift.json +++ b/src/driftpy/idl/drift.json @@ -1,5 +1,5 @@ { - "version": "2.85.0", + "version": "2.86.0", "name": "drift", "instructions": [ { @@ -12762,8 +12762,5 @@ "name": "InvalidOpenbookV2Market", "msg": "InvalidOpenbookV2Market" } - ], - "metadata": { - "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" - } + ] } \ No newline at end of file diff --git a/src/driftpy/math/fuel.py b/src/driftpy/math/fuel.py new file mode 100644 index 00000000..48304d86 --- /dev/null +++ b/src/driftpy/math/fuel.py @@ -0,0 +1,56 @@ +from driftpy.types import SpotMarketAccount, PerpMarketAccount +from driftpy.constants.numeric_constants import QUOTE_PRECISION, FUEL_WINDOW + + +def calculate_insurance_fuel_bonus( + spot_market: SpotMarketAccount, token_stake_amount: int, fuel_bonus_numerator: int +) -> int: + insurance_fund_fuel = ( + abs(token_stake_amount) * fuel_bonus_numerator + ) * spot_market.fuel_boost_insurance + insurace_fund_fuel_per_day = insurance_fund_fuel // FUEL_WINDOW + insurance_fund_fuel_scaled = insurace_fund_fuel_per_day // (QUOTE_PRECISION // 10) + + return insurance_fund_fuel_scaled + + +def calculate_spot_fuel_bonus( + spot_market: SpotMarketAccount, signed_token_value: int, fuel_bonus_numerator: int +) -> int: + spot_fuel_scaled: int + + # dust + if abs(signed_token_value) <= QUOTE_PRECISION: + spot_fuel_scaled = 0 + elif signed_token_value > 0: + deposit_fuel = ( + abs(signed_token_value) * fuel_bonus_numerator + ) * spot_market.fuel_boost_deposits + deposit_fuel_per_day = deposit_fuel // FUEL_WINDOW + spot_fuel_scaled = deposit_fuel_per_day // (QUOTE_PRECISION // 10) + else: + borrow_fuel = ( + abs(signed_token_value) * fuel_bonus_numerator + ) * spot_market.fuel_boost_borrows + borrow_fuel_per_day = borrow_fuel // FUEL_WINDOW + spot_fuel_scaled = borrow_fuel_per_day // (QUOTE_PRECISION // 10) + + return spot_fuel_scaled + + +def calculate_perp_fuel_bonus( + perp_market: PerpMarketAccount, base_asset_value: int, fuel_bonus_numerator: int +) -> int: + perp_fuel_scaled: int + + # dust + if abs(base_asset_value) <= QUOTE_PRECISION: + perp_fuel_scaled = 0 + else: + perp_fuel = ( + abs(base_asset_value) * fuel_bonus_numerator + ) * perp_market.fuel_boost_position + perp_fuel_per_day = perp_fuel // FUEL_WINDOW + perp_fuel_scaled = perp_fuel_per_day // (QUOTE_PRECISION // 10) + + return perp_fuel_scaled diff --git a/src/driftpy/types.py b/src/driftpy/types.py index 967bec2f..32fcf38b 100644 --- a/src/driftpy/types.py +++ b/src/driftpy/types.py @@ -700,9 +700,9 @@ class PerpMarketAccount: padding1: int = 0 quote_spot_market_index: Optional[int] = None fee_adjustment: Optional[int] = None - fuel_boost_position: Optional[int] = None fuel_boost_taker: Optional[int] = None fuel_boost_maker: Optional[int] = None + fuel_boost_position: Optional[int] = None padding: list[int] = field(default_factory=lambda: [0] * 43) @@ -789,7 +789,6 @@ class SpotMarketAccount: fuel_boost_deposits: Optional[int] = None fuel_boost_borrows: Optional[int] = None fuel_boost_taker: Optional[int] = None - min_borrow_rate: Optional[int] = None fuel_boost_maker: Optional[int] = None fuel_boost_insurance: Optional[int] = None padding: list[int] = field(default_factory=lambda: [0] * 42) @@ -870,6 +869,7 @@ class UserAccount: has_open_order: bool open_auctions: int has_open_auction: bool + last_fuel_bonus_update_ts: int padding: list[int] = field(default_factory=lambda: [0] * 21) @@ -906,15 +906,15 @@ class UserStatsAccount: number_of_sub_accounts_created: int is_referrer: bool disable_update_perp_bid_ask_twap: bool - padding1: list[int] = field(default_factory=lambda: [0] * 2), - fuel_insurance: int - fuel_deposits: int - fuel_borrows: int - fuel_positions: int - fuel_taker: int - fuel_maker: int - if_staked_gov_token_amount: int - last_fuel_if_bonus_update_ts: int + padding1: list[int] = (field(default_factory=lambda: [0] * 2),) + last_fuel_bonus_update_ts: int = (0,) + fuel_insurance: int = (0,) + fuel_deposits: int = (0,) + fuel_borrows: int = (0,) + fuel_positions: int = (0,) + fuel_taker: int = (0,) + fuel_maker: int = (0,) + if_staked_gov_token_amount: int = (0,) padding: list[int] = field(default_factory=lambda: [0] * 12) diff --git a/update_idl.sh b/update_idl.sh index 6be6b055..c035c2d1 100644 --- a/update_idl.sh +++ b/update_idl.sh @@ -1,3 +1,4 @@ +git submodule update --remote --merge --recursive && cd protocol-v2/ && anchor build && cp target/idl/* ../src/driftpy/idl/ \ No newline at end of file