Skip to content

chester/add isolated deposits#2158

Merged
LukasDeco merged 7 commits intomasterfrom
chester/add-isolated-deposits
Mar 23, 2026
Merged

chester/add isolated deposits#2158
LukasDeco merged 7 commits intomasterfrom
chester/add-isolated-deposits

Conversation

@ChesterSim
Copy link
Collaborator

@ChesterSim ChesterSim commented Mar 23, 2026

Summary

Several SDK methods that compute aggregate account value and leverage (getNetUsdValue, getTotalAssetValue, getLeverageComponents) did not account for USDC deposits held in isolated perp positions (PerpPosition.isolatedPositionScaledBalance). This caused under-reporting of total account value, incorrect leverage/margin ratio calculations, and incorrect all-time PnL. This PR fixes that by summing isolated deposits into these value/leverage methods.

Health/margin methods (e.g. getTotalCollateral, getMarginCalculation) are intentionally not changed — isolated positions have their own independent health tracked via isolatedMarginCalculations.

Changes

New helper method:

  • getTotalIsolatedPositionDeposits() — sums getIsolatePerpPositionTokenAmount() across all active perp positions to get total isolated USDC deposit value

Methods fixed (sdk/src/user.ts):

  • getNetUsdValue() — now adds isolated deposits (cascading fix to getTotalAllTimePnl())
  • getTotalAssetValue() — now adds isolated deposits
  • getLeverageComponents() aggregate path (no perpMarketIndex) — adds isolated deposits to spotAssetValue (cascading fix to getLeverage(), getMarginRatio(), getMaxLeverageForPerp(), getMaxLeverageForSpot(), getMaxSwapAmount(), calculateMaxSwapOutput())

Tests (sdk/tests/user/test.ts):

  • 4 new tests verifying isolated deposits are included in each modified method

Test plan

  • getTotalIsolatedPositionDeposits correctly sums multiple isolated position deposits
  • getNetUsdValue returns cross-margin spot value + unrealized PnL + isolated deposits
  • getTotalAssetValue returns spot asset value + unrealized PnL + isolated deposits
  • getLeverageComponents aggregate path includes isolated deposits in spotAssetValue
  • Existing tests pass (no regressions in health/margin methods)

Summary by CodeRabbit

  • New Features

    • Compute and sum isolated position deposits (valued via spot oracle) and include them in total asset value, net USD value, and default leverage component calculations (excluded when a specific margin category is requested).
  • Tests

    • Added tests covering isolated deposit aggregation, oracle-driven valuation (including a non-1.0/depeg case), and their effects on asset, net USD, and leverage calculations across relevant scenarios.

ChesterSim and others added 2 commits March 23, 2026 21:13
Adds a new public method that sums isolated USDC deposit values across all
active perp positions by aggregating getIsolatePerpPositionTokenAmount results.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…alue, and getLeverageComponents

Wire getTotalIsolatedPositionDeposits() into the three aggregate value methods so that
isolated perp position deposits are counted in net USD value, total asset value, and
leverage component calculations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 23, 2026

Walkthrough

Added User.getTotalIsolatedPositionDeposits() and updated User valuation methods so isolated perp-position deposits are included in leverage components, total asset value, and net USD value; tests added to validate valuation, oracle pricing, inclusion/exclusion semantics, and margin behaviors.

Changes

Cohort / File(s) Summary
Core implementation
sdk/src/user.ts
Added getTotalIsolatedPositionDeposits(): BN. Updated getLeverageComponents() (default branch), getTotalAssetValue() (default/marginCategory undefined), and getNetUsdValue() to incorporate isolated perp-position deposits into spot/asset/net computations.
Unit tests — user
sdk/tests/user/test.ts
Imported makeMockUser; added tests that create isolated perp-position deposits via perpPositions[*].isolatedPositionScaledBalance and active baseAssetAmount, asserting oracle-priced USD sums and effects on getTotalIsolatedPositionDeposits(), getNetUsdValue(), getTotalAssetValue() (default vs 'Initial'), and getLeverageComponents() (default vs exclusion).
Unit tests — margin calculations
sdk/tests/user/marginCalculations.test.ts
New test suite validating cross vs isolated collateral behavior, ordering of margin requirements, isolated-collateral exclusion from cross buckets, isolated collateral retrieval via getTotalCollateral(...), isolated free collateral logic per market, and liquidation price non-negativity under configured scenarios.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant PerpPos as PerpPosition (loop)
    participant PerpMarket as PerpMarket
    participant SpotMarket as SpotMarket
    participant Oracle as Oracle

    User->>PerpPos: iterate active perp positions
    PerpPos-->>User: return position with isolatedPositionScaledBalance
    User->>PerpMarket: fetch perp market by marketIndex
    PerpMarket-->>User: returns perp market (includes quoteSpotMarketIndex)
    User->>SpotMarket: fetch quote spot market
    SpotMarket-->>User: returns spot market (includes oracle data)
    User->>Oracle: derive strict oracle price from spot market
    Oracle-->>User: strict price
    User->>User: convert isolatedPositionScaledBalance -> quote token amount -> USD using price
    User->>User: sum across positions -> getTotalIsolatedPositionDeposits()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through markets, counted each tiny seed,
From isolated vaults to oracle-feed,
I priced and summed beneath the moonlit code,
A ledger neat where balances glowed,
Rabbit math done — in one soft leap.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'chester/add isolated deposits' clearly and directly describes the main feature added: support for isolated deposits in SDK methods, which aligns with the PR's core objective of fixing methods to include USDC deposits in isolated perp positions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
sdk/src/user.ts (1)

381-414: Consider centralizing isolated-deposit valuation logic.

This isolatedPositionScaledBalance -> tokenAmount -> USD value flow now exists here, in the single-market getLeverageComponents() branch, and again in getMarginCalculation(). A shared private helper would make future pricing changes much safer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/src/user.ts` around lines 381 - 414, Centralize the isolated-position
valuation: extract the repeated flow (isolatedPositionScaledBalance ->
getTokenAmount -> getStrictTokenValue using quoteSpotMarket,
quoteOraclePriceData/StrictOracle) into a single private helper (e.g.,
computeIsolatedDepositUsdValue or isolatedScaledBalanceToUsd) and call it from
getTotalIsolatedPositionDeposits, the single-market branch in
getLeverageComponents, and getMarginCalculation; the helper should accept the
perpPosition (or isolatedPositionScaledBalance and market indices) plus
driftClient/getOracleData accessors and return a BN USD value so callers simply
sum or add the returned BN.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sdk/src/user.ts`:
- Around line 1823-1828: getLeverageComponents currently adds
getTotalIsolatedPositionDeposits() into spotAssetValue for every marginCategory,
which diverges from getTotalAssetValue(marginCategory) that only includes
isolated deposits when marginCategory === undefined; update
getLeverageComponents to only add isolatedDeposits to spotAssetValue when
marginCategory is undefined (same condition used by getTotalAssetValue), keeping
calculateLeverageFromComponents behavior consistent; refer to
getLeverageComponents, getTotalAssetValue, calculateLeverageFromComponents, and
getTotalIsolatedPositionDeposits and adjust the spotAssetValue +
isolatedDeposits logic to be conditional.

---

Nitpick comments:
In `@sdk/src/user.ts`:
- Around line 381-414: Centralize the isolated-position valuation: extract the
repeated flow (isolatedPositionScaledBalance -> getTokenAmount ->
getStrictTokenValue using quoteSpotMarket, quoteOraclePriceData/StrictOracle)
into a single private helper (e.g., computeIsolatedDepositUsdValue or
isolatedScaledBalanceToUsd) and call it from getTotalIsolatedPositionDeposits,
the single-market branch in getLeverageComponents, and getMarginCalculation; the
helper should accept the perpPosition (or isolatedPositionScaledBalance and
market indices) plus driftClient/getOracleData accessors and return a BN USD
value so callers simply sum or add the returned BN.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 02bb1c09-6315-41fd-896c-0e346f07eadd

📥 Commits

Reviewing files that changed from the base of the PR and between 9164dd3 and 00fd99a.

📒 Files selected for processing (2)
  • sdk/src/user.ts
  • sdk/tests/user/test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdk/tests/user/test.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
sdk/tests/user/marginCalculations.test.ts (2)

193-199: Avoid brittle long positional-argument calls in tests.

These invocations are hard to read and easy to mis-order. A tiny wrapper/helper with named intent would make failures easier to reason about.

🧩 Suggested refactor
+const getIsolatedCollateral = (user: any, perpMarketIndex: number) =>
+  user.getTotalCollateral('Maintenance', false, true, undefined, perpMarketIndex);
+
+const getIsolatedLiqPrice = (user: any, perpMarketIndex: number) =>
+  user.liquidationPrice(
+    perpMarketIndex,
+    ZERO,
+    ZERO,
+    'Maintenance',
+    false,
+    ZERO,
+    false,
+    'Isolated'
+  );

Also applies to: 308-317

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/tests/user/marginCalculations.test.ts` around lines 193 - 199, The tests
call mockUser.getTotalCollateral with many positional args which is brittle and
hard to read; refactor by adding a small test helper or wrapper (e.g., a helper
function like getTotalCollateralForTests or a wrapper around
mockUser.getTotalCollateral) that accepts a descriptive options object or named
parameters and forwards them to mockUser.getTotalCollateral, then replace the
long positional calls (including the occurrences around the second range) with
calls to that helper to improve readability and reduce ordering errors.

23-39: Reduce repeated mock setup with a small fixture builder.

The repeated clone-and-build blocks make the suite harder to maintain. A local helper would keep tests shorter and reduce edit drift.

♻️ Suggested refactor
+const buildMockUser = async (
+  mutate?: (ctx: {
+    myMockPerpMarkets: typeof mockPerpMarkets;
+    myMockSpotMarkets: typeof mockSpotMarkets;
+    myMockUserAccount: typeof mockUserAccount;
+  }) => void
+) => {
+  const myMockPerpMarkets = _.cloneDeep(mockPerpMarkets);
+  const myMockSpotMarkets = _.cloneDeep(mockSpotMarkets);
+  const myMockUserAccount = _.cloneDeep(mockUserAccount);
+  mutate?.({ myMockPerpMarkets, myMockSpotMarkets, myMockUserAccount });
+  return makeMockUserFromHelpers(
+    myMockPerpMarkets,
+    myMockSpotMarkets,
+    myMockUserAccount,
+    DEFAULT_ORACLES,
+    DEFAULT_ORACLES
+  );
+};

Also applies to: 47-73, 82-99, 104-122, 130-160, 167-191, 204-228, 237-268, 276-307

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/tests/user/marginCalculations.test.ts` around lines 23 - 39, Extract the
repeated clone-and-setup into a small local fixture builder function (e.g.,
buildMockUser) that accepts optional overrides for perpMarkets, spotMarkets,
userAccount adjustments and internally clones
mockPerpMarkets/mockSpotMarkets/mockUserAccount, applies modifications like
setting spotPositions[0].marketIndex, balanceType = SpotBalanceType.DEPOSIT and
scaledBalance = new BN(...).mul(SPOT_MARKET_BALANCE_PRECISION), then calls
makeMockUserFromHelpers(myMockPerpMarkets, myMockSpotMarkets, myMockUserAccount,
DEFAULT_ORACLES, DEFAULT_ORACLES) and returns the result; replace each repeated
block that constructs myMockPerpMarkets, myMockSpotMarkets, myMockUserAccount
and calls makeMockUserFromHelpers with calls to this builder (passing only the
small differences per test).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@sdk/tests/user/marginCalculations.test.ts`:
- Around line 193-199: The tests call mockUser.getTotalCollateral with many
positional args which is brittle and hard to read; refactor by adding a small
test helper or wrapper (e.g., a helper function like getTotalCollateralForTests
or a wrapper around mockUser.getTotalCollateral) that accepts a descriptive
options object or named parameters and forwards them to
mockUser.getTotalCollateral, then replace the long positional calls (including
the occurrences around the second range) with calls to that helper to improve
readability and reduce ordering errors.
- Around line 23-39: Extract the repeated clone-and-setup into a small local
fixture builder function (e.g., buildMockUser) that accepts optional overrides
for perpMarkets, spotMarkets, userAccount adjustments and internally clones
mockPerpMarkets/mockSpotMarkets/mockUserAccount, applies modifications like
setting spotPositions[0].marketIndex, balanceType = SpotBalanceType.DEPOSIT and
scaledBalance = new BN(...).mul(SPOT_MARKET_BALANCE_PRECISION), then calls
makeMockUserFromHelpers(myMockPerpMarkets, myMockSpotMarkets, myMockUserAccount,
DEFAULT_ORACLES, DEFAULT_ORACLES) and returns the result; replace each repeated
block that constructs myMockPerpMarkets, myMockSpotMarkets, myMockUserAccount
and calls makeMockUserFromHelpers with calls to this builder (passing only the
small differences per test).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2d351669-d67f-4e03-8463-e06a2b7e6de9

📥 Commits

Reviewing files that changed from the base of the PR and between c817a63 and 95cb249.

📒 Files selected for processing (1)
  • sdk/tests/user/marginCalculations.test.ts

@LukasDeco LukasDeco merged commit bba1181 into master Mar 23, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants