🧹 Add USD pricing for Drift perp markets without spot mints#75
🧹 Add USD pricing for Drift perp markets without spot mints#75
Conversation
…ints. Instead of ignoring these perp positions, the value is calculated using the oracle price from the perp market account data and attributed to the base quote token (USDC). Co-authored-by: zknpr <96851588+zknpr@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
The adapter at projects/neutral-trade exports TVL: |
Greptile SummaryThis PR completes a long-standing Key points:
Confidence Score: 3/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[getTvl: iterate perpPositions] --> B{meta.mint exists?}
B -- Yes --> C[api.add baseTokenMint, baseBalance]
B -- No\ne.g. HYPE-PERP --> D[getPerpMarketOraclePrice\nread PerpMarket buffer at offset 72\namm.historical_oracle_data.last_oracle_price]
D --> E{oraclePrice valid?\nNO GUARD - NEW RISK}
E -- price > 0 --> F["usdValue = base_asset_amount × oraclePrice / 10^9"]
E -- price ≤ 0 --> G[Silent wrong/zero TVL]
F --> H[api.add USDC mint, usdValue]
C --> I[api.add USDC mint, quoteBalance]
H --> I
Last reviewed commit: 4d12bb6 |
| if (!accountInfo) { | ||
| throw new Error(`No account info found for market`); | ||
| } | ||
| return accountInfo.data.readBigInt64LE(72); |
There was a problem hiding this comment.
Undocumented magic offset — stale cached price
The hardcoded offset 72 corresponds to amm.historical_oracle_data.last_oracle_price in the Drift PerpMarket account layout:
[0–7] discriminator (8 bytes)
[8–39] pubkey (32 bytes)
[40–71] amm.oracle (32 bytes, the oracle Pubkey)
[72–79] amm.historical_oracle_data.last_oracle_price ← this field
Two things worth noting:
-
This is a cached/stale price stored in the market account, updated the last time any instruction touched the market (trade, liquidation, crank). For frequently traded markets like HYPE-PERP this is fine, but for less active perps it could lag the true market price. The value is not a live Pyth/Switchboard oracle read.
-
The bare
72gives no indication of its meaning to future readers. Consider extracting it to a named constant:
| return accountInfo.data.readBigInt64LE(72); | |
| const ORACLE_PRICE_OFFSET = 72; // amm.historical_oracle_data.last_oracle_price (i64) | |
| return accountInfo.data.readBigInt64LE(ORACLE_PRICE_OFFSET); |
| function getPerpMarketOraclePrice(accountInfo) { | ||
| if (!accountInfo) { | ||
| throw new Error(`No account info found for market`); | ||
| } | ||
| return accountInfo.data.readBigInt64LE(72); | ||
| } |
There was a problem hiding this comment.
No guard against a zero or negative oracle price
readBigInt64LE returns a signed 64-bit BigInt. If the perp market account is uninitialised, freshly created, or has an invalid state, last_oracle_price could be 0n or negative. Multiplying base_asset_amount by such a value and dividing will silently produce a zero or wrong-sign USD contribution, corrupting the TVL without any visible error.
The existing getPerpMarketFundingRates function has the same absence of value validation, but that computation is additive PnL and the error is bounded. Here the entire notional USD value of a base-asset leg depends on this single field.
Consider adding a guard before returning:
| function getPerpMarketOraclePrice(accountInfo) { | |
| if (!accountInfo) { | |
| throw new Error(`No account info found for market`); | |
| } | |
| return accountInfo.data.readBigInt64LE(72); | |
| } | |
| function getPerpMarketOraclePrice(accountInfo) { | |
| if (!accountInfo) { | |
| throw new Error(`No account info found for market`); | |
| } | |
| const price = accountInfo.data.readBigInt64LE(72); | |
| if (price <= 0n) { | |
| throw new Error(`Invalid oracle price ${price} read from market account`); | |
| } | |
| return price; | |
| } |
🎯 What: The code health issue addressed
Adds logic to fetch the oracle price and calculate USD value for Drift perp positions that lack a corresponding spot mint (e.g., HYPE-PERP).
💡 Why: How this improves maintainability
Previously these positions were skipped, leaving a
TODOcomment. By reading the oracle price from the market account buffer (which is already prefetched), we correctly attribute this value to USDC without adding complex external calls.✅ Verification: How you confirmed the change is safe
Ran
node test.js projects/neutral-tradeto verify that theHYPE-PERPand other mint-less positions are correctly priced and that the TVL accumulates accurately, and that there are no runtime crashes or mixups.✨ Result: The improvement achieved
Eliminated the "skipped base leg" workaround, completing the original author's TODO.
PR created automatically by Jules for task 7359130521910270343 started by @zknpr