Skip to content
Open
Changes from all commits
Commits
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
9 changes: 6 additions & 3 deletions projects/neutral-trade/utils/drift.js
Original file line number Diff line number Diff line change
Expand Up @@ -2099,9 +2099,12 @@ async function getTvl(api, driftVaultAddresses) {
if (baseTokenMint) {
api.add(baseTokenMint, baseBalance);
} else {
// e.g. HYPE-PERP: no SPL spot mint; skip base leg to avoid "missing token"
// console.log(`No spot mint for perp market ${position.market_index} (${meta?.name}); skipping base leg`);
// TODO: find usd price and api.add(getTokenMintFromMarketIndex(0), usdValue);
const perpAccount = perpAccountMap[position.market_index];
if (perpAccount && perpAccount.data) {
const price = perpAccount.data.readBigInt64LE(72);
const usdValue = (position.base_asset_amount * price) / BigInt(10 ** 9);
Comment on lines +2104 to +2105
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong buffer offset reads 5-minute TWAP, not live oracle price

Based on the Drift v2 PerpMarket account layout, the AMM struct begins right after the 8-byte Anchor discriminator, with the following field order:

Global Offset Field
8 AMM.oracle (Pubkey, 32 bytes)
40 HistoricalOracleData.last_oracle_pricelive oracle price
48 HistoricalOracleData.last_oracle_conf
56 HistoricalOracleData.last_oracle_delay
64 HistoricalOracleData.last_oracle_price_twap (1-hour TWAP)
72 HistoricalOracleData.last_oracle_price_twap_5minwhat the code reads
80 HistoricalOracleData.last_oracle_price_twap_ts

Reading at offset 72 picks up last_oracle_price_twap_5min (the 5-minute time-weighted average price), not the live oracle price described in the PR. During periods of high volatility — exactly the times when HYPE-PERP is most active — this TWAP can diverge significantly from the actual price and will cause the TVL to be measurably incorrect.

The live oracle price is at offset 40. If a TWAP is intentionally preferred for stability, that should be documented with a comment.

Suggested change
const price = perpAccount.data.readBigInt64LE(72);
const usdValue = (position.base_asset_amount * price) / BigInt(10 ** 9);
const price = perpAccount.data.readBigInt64LE(40); // AMM.historical_oracle_data.last_oracle_price (offset = 8 discriminator + 32 oracle pubkey)
const usdValue = (position.base_asset_amount * price) / BigInt(10 ** 9);

api.add(getTokenMintFromMarketIndex(0), usdValue);
}
Comment on lines +2102 to +2107
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic numbers with no derivation comments

Unlike getPerpMarketFundingRates, which documents its offset as:

const CUMULATIVE_FUNDING_OFFSET = 8 + 48 + 32 + 256 + (16 * 15) + 24;

the new code uses bare magic numbers 72 and 10 ** 9 without any comment explaining what struct field is at that offset or what precision constant is being applied. This makes it very hard to verify correctness or maintain in the future.

Consider adding an explanatory constant and comment, similar to the existing pattern:

// Drift v2 PerpMarket layout: 8 (discriminator) + 32 (AMM.oracle pubkey) = 40
const ORACLE_PRICE_OFFSET = 40; // AMM.historical_oracle_data.last_oracle_price
// base_asset_amount is in BASE_PRECISION (1e9), price is in PRICE_PRECISION (1e6)
// USD value in USDC raw (6 decimals) = (base/1e9) * (price/1e6) * 1e6 = base * price / 1e9
const price = perpAccount.data.readBigInt64LE(ORACLE_PRICE_OFFSET);
const usdValue = (position.base_asset_amount * price) / BigInt(10 ** 9);

}

const quoteTokenMint = getTokenMintFromMarketIndex(0);
Expand Down
Loading