@velocity-exchange/sdk 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/lib/browser/adminClient.d.ts +13 -10
  2. package/lib/browser/adminClient.js +16 -21
  3. package/lib/browser/decode/user.js +3 -1
  4. package/lib/browser/equityFloorManager.d.ts +154 -0
  5. package/lib/browser/equityFloorManager.js +283 -0
  6. package/lib/browser/idl/velocity.d.ts +85 -10
  7. package/lib/browser/idl/velocity.json +85 -10
  8. package/lib/browser/index.d.ts +4 -0
  9. package/lib/browser/index.js +5 -0
  10. package/lib/browser/jupiter/jupiterClient.d.ts +31 -27
  11. package/lib/browser/jupiter/jupiterClient.js +84 -37
  12. package/lib/browser/math/margin.d.ts +28 -0
  13. package/lib/browser/math/margin.js +43 -1
  14. package/lib/browser/math/superStake.d.ts +9 -9
  15. package/lib/browser/math/superStake.js +8 -8
  16. package/lib/browser/swap/UnifiedSwapClient.d.ts +38 -76
  17. package/lib/browser/swap/UnifiedSwapClient.js +53 -110
  18. package/lib/browser/swap/routeInstructions.d.ts +19 -0
  19. package/lib/browser/swap/routeInstructions.js +43 -0
  20. package/lib/browser/swap/types.d.ts +196 -0
  21. package/lib/browser/swap/types.js +88 -0
  22. package/lib/browser/titan/titanClient.d.ts +37 -67
  23. package/lib/browser/titan/titanClient.js +160 -103
  24. package/lib/browser/types.d.ts +6 -2
  25. package/lib/browser/types.js +2 -0
  26. package/lib/browser/user.d.ts +43 -18
  27. package/lib/browser/user.js +94 -45
  28. package/lib/browser/velocityClient.d.ts +86 -103
  29. package/lib/browser/velocityClient.js +157 -298
  30. package/lib/node/adminClient.d.ts +13 -10
  31. package/lib/node/adminClient.d.ts.map +1 -1
  32. package/lib/node/adminClient.js +16 -21
  33. package/lib/node/decode/user.d.ts.map +1 -1
  34. package/lib/node/decode/user.js +3 -1
  35. package/lib/node/equityFloorManager.d.ts +155 -0
  36. package/lib/node/equityFloorManager.d.ts.map +1 -0
  37. package/lib/node/equityFloorManager.js +283 -0
  38. package/lib/node/idl/velocity.d.ts +85 -10
  39. package/lib/node/idl/velocity.d.ts.map +1 -1
  40. package/lib/node/idl/velocity.json +85 -10
  41. package/lib/node/index.d.ts +4 -0
  42. package/lib/node/index.d.ts.map +1 -1
  43. package/lib/node/index.js +5 -0
  44. package/lib/node/jupiter/jupiterClient.d.ts +31 -27
  45. package/lib/node/jupiter/jupiterClient.d.ts.map +1 -1
  46. package/lib/node/jupiter/jupiterClient.js +84 -37
  47. package/lib/node/math/margin.d.ts +28 -0
  48. package/lib/node/math/margin.d.ts.map +1 -1
  49. package/lib/node/math/margin.js +43 -1
  50. package/lib/node/math/superStake.d.ts +9 -9
  51. package/lib/node/math/superStake.d.ts.map +1 -1
  52. package/lib/node/math/superStake.js +8 -8
  53. package/lib/node/swap/UnifiedSwapClient.d.ts +38 -76
  54. package/lib/node/swap/UnifiedSwapClient.d.ts.map +1 -1
  55. package/lib/node/swap/UnifiedSwapClient.js +53 -110
  56. package/lib/node/swap/routeInstructions.d.ts +20 -0
  57. package/lib/node/swap/routeInstructions.d.ts.map +1 -0
  58. package/lib/node/swap/routeInstructions.js +43 -0
  59. package/lib/node/swap/types.d.ts +197 -0
  60. package/lib/node/swap/types.d.ts.map +1 -0
  61. package/lib/node/swap/types.js +88 -0
  62. package/lib/node/titan/titanClient.d.ts +37 -67
  63. package/lib/node/titan/titanClient.d.ts.map +1 -1
  64. package/lib/node/titan/titanClient.js +160 -103
  65. package/lib/node/types.d.ts +6 -2
  66. package/lib/node/types.d.ts.map +1 -1
  67. package/lib/node/types.js +2 -0
  68. package/lib/node/user.d.ts +43 -18
  69. package/lib/node/user.d.ts.map +1 -1
  70. package/lib/node/user.js +94 -45
  71. package/lib/node/velocityClient.d.ts +86 -103
  72. package/lib/node/velocityClient.d.ts.map +1 -1
  73. package/lib/node/velocityClient.js +157 -298
  74. package/package.json +1 -1
@@ -724,12 +724,10 @@ class User {
724
724
  * @param marginCategory `'Initial'` or `'Maintenance'` asset/liability weights; omit for unweighted (100%) values.
725
725
  * @param liquidationBuffer Optional buffer (MARGIN_PRECISION, 1e4) added to the liability weight side.
726
726
  * @param includeOpenOrders If false, ignores open bids/asks and only counts the current balance (faster, less conservative).
727
- * @param strict Use the worse of live oracle price vs 5-minute TWAP. Defaults to false.
728
- * @param now Unix timestamp (seconds) used for TWAP staleness when `strict` is set; defaults to current time.
727
+ * @param strict Use the worse of live oracle price vs the market's stored 5-minute TWAP. Defaults to false.
729
728
  * @returns `{ totalAssetValue, totalLiabilityValue }`, both QUOTE_PRECISION (1e6) and non-negative.
730
729
  */
731
- getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict = false, now) {
732
- now = now || new anchor_1.BN(new Date().getTime() / 1000);
730
+ getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict = false) {
733
731
  let netQuoteValue = numericConstants_1.ZERO;
734
732
  let totalAssetValue = numericConstants_1.ZERO;
735
733
  let totalLiabilityValue = numericConstants_1.ZERO;
@@ -744,11 +742,11 @@ class User {
744
742
  }
745
743
  const spotMarketAccount = this.velocityClient.getSpotMarketAccountOrThrow(spotPosition.marketIndex);
746
744
  const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
747
- let twap5min;
748
- if (strict) {
749
- twap5min = (0, oracles_1.calculateLiveOracleTwap)(spotMarketAccount.historicalOracleData, oraclePriceData, now, numericConstants_1.FIVE_MINUTE // 5MIN
750
- );
751
- }
745
+ // mirrors margin.rs: strict mode prices against the market's *stored*
746
+ // 5min TWAP, not a live-projected one
747
+ const twap5min = strict
748
+ ? spotMarketAccount.historicalOracleData.lastOraclePriceTwap5Min
749
+ : undefined;
752
750
  const strictOraclePrice = new strictOraclePrice_1.StrictOraclePrice(oraclePriceData.price, twap5min);
753
751
  if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX &&
754
752
  countForQuote) {
@@ -813,8 +811,8 @@ class User {
813
811
  return { totalAssetValue, totalLiabilityValue };
814
812
  }
815
813
  /** Convenience wrapper around `getSpotMarketAssetAndLiabilityValue` returning only `totalLiabilityValue`. See that method for parameter semantics. Returns QUOTE_PRECISION (1e6). */
816
- getSpotMarketLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict = false, now) {
817
- const { totalLiabilityValue } = this.getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict, now);
814
+ getSpotMarketLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict = false) {
815
+ const { totalLiabilityValue } = this.getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, liquidationBuffer, includeOpenOrders, strict);
818
816
  return totalLiabilityValue;
819
817
  }
820
818
  /** Thin wrapper around the `math/spotBalance` `getSpotLiabilityValue` helper that supplies the user's `maxMarginRatio`. Returns QUOTE_PRECISION (1e6), negative. */
@@ -822,8 +820,8 @@ class User {
822
820
  return (0, spotBalance_2.getSpotLiabilityValue)(tokenAmount, strictOraclePrice, spotMarketAccount, this.getUserAccountOrThrow().maxMarginRatio, marginCategory, liquidationBuffer);
823
821
  }
824
822
  /** Convenience wrapper around `getSpotMarketAssetAndLiabilityValue` returning only `totalAssetValue`. See that method for parameter semantics. Returns QUOTE_PRECISION (1e6), non-negative. */
825
- getSpotMarketAssetValue(marketIndex, marginCategory, includeOpenOrders, strict = false, now) {
826
- const { totalAssetValue } = this.getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, undefined, includeOpenOrders, strict, now);
823
+ getSpotMarketAssetValue(marketIndex, marginCategory, includeOpenOrders, strict = false) {
824
+ const { totalAssetValue } = this.getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, undefined, includeOpenOrders, strict);
827
825
  return totalAssetValue;
828
826
  }
829
827
  /** Thin wrapper around the `math/spotBalance` `getSpotAssetValue` helper that supplies the user's `maxMarginRatio`. Returns QUOTE_PRECISION (1e6), non-negative. */
@@ -831,8 +829,8 @@ class User {
831
829
  return (0, spotBalance_2.getSpotAssetValue)(tokenAmount, strictOraclePrice, spotMarketAccount, this.getUserAccountOrThrow().maxMarginRatio, marginCategory);
832
830
  }
833
831
  /** Net spot value (`totalAssetValue - totalLiabilityValue`) for a single spot market. See `getSpotMarketAssetAndLiabilityValue` for parameter semantics. Returns QUOTE_PRECISION (1e6), can be negative. */
834
- getSpotPositionValue(marketIndex, marginCategory, includeOpenOrders, strict = false, now) {
835
- const { totalAssetValue, totalLiabilityValue } = this.getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, undefined, includeOpenOrders, strict, now);
832
+ getSpotPositionValue(marketIndex, marginCategory, includeOpenOrders, strict = false) {
833
+ const { totalAssetValue, totalLiabilityValue } = this.getSpotMarketAssetAndLiabilityValue(marketIndex, marginCategory, undefined, includeOpenOrders, strict);
836
834
  return totalAssetValue.sub(totalLiabilityValue);
837
835
  }
838
836
  /**
@@ -899,31 +897,71 @@ class User {
899
897
  return marginCalc.totalCollateral;
900
898
  }
901
899
  /**
902
- * True when the account has an admin-set `equityFloor` and its cross-margin
903
- * total collateral is below it. While below the floor, the program rejects
904
- * risk-increasing order placement and fills, withdrawals, and transfers out
905
- * of the account (`EquityBelowFloor`); reduce-only activity stays allowed.
906
- * Mirrors `User::is_below_equity_floor` on-chain.
907
- * @param strict Use TWAP-bounded oracle pricing, matching the withdraw path. Defaults to false.
900
+ * True when the account has an admin-set `equityFloor` and its net equity
901
+ * (`getNetUsdValue`: unweighted assets and perp PnL minus unweighted spot
902
+ * liabilities, at live oracle prices) is below it. This is the trip
903
+ * threshold of the permissionless `tripEquityFloorBreaker`; action gating
904
+ * happens at `equityFloor + equityFloorBuffer` (see
905
+ * `isBelowBufferedEquityFloor`). Mirrors `User::is_below_equity_floor`
906
+ * onchain.
907
+ */
908
+ isBelowEquityFloor() {
909
+ const equityFloor = this.getUserAccountOrThrow().equityFloor;
910
+ if (equityFloor.lte(numericConstants_1.ZERO)) {
911
+ return false;
912
+ }
913
+ return this.getNetUsdValue().lt(equityFloor);
914
+ }
915
+ /**
916
+ * The equity required by risk-increasing actions:
917
+ * `equityFloor + equityFloorBuffer` (QUOTE_PRECISION). Mirrors
918
+ * `User::buffered_equity_floor` on-chain. Meaningless while
919
+ * `equityFloor` is 0 (the checks are disabled).
920
+ */
921
+ getBufferedEquityFloor() {
922
+ const userAccount = this.getUserAccountOrThrow();
923
+ return userAccount.equityFloor.add(userAccount.equityFloorBuffer);
924
+ }
925
+ /**
926
+ * True when the account has an admin-set `equityFloor` and its net equity
927
+ * (`getNetUsdValue`) is below `equityFloor + equityFloorBuffer`. While
928
+ * below, the program rejects risk-increasing order placement and fills,
929
+ * withdrawals, and transfers out of the account (`EquityBelowFloor`);
930
+ * reduce-only activity stays allowed. Mirrors
931
+ * `User::is_below_buffered_equity_floor` on-chain.
908
932
  */
909
- isBelowEquityFloor(strict = false) {
933
+ isBelowBufferedEquityFloor() {
910
934
  const equityFloor = this.getUserAccountOrThrow().equityFloor;
911
935
  if (equityFloor.lte(numericConstants_1.ZERO)) {
912
936
  return false;
913
937
  }
914
- return this.getTotalCollateral('Initial', strict).lt(equityFloor);
938
+ return this.getNetUsdValue().lt(this.getBufferedEquityFloor());
915
939
  }
916
940
  /**
917
- * Cross-margin total collateral in excess of the admin-set `equityFloor`,
941
+ * Net equity (`getNetUsdValue`) in excess of the admin-set `equityFloor`,
918
942
  * floored at zero (QUOTE_PRECISION). Unbounded (`null`) when no floor is set.
919
- * @param strict Use TWAP-bounded oracle pricing. Defaults to false.
943
+ * This is headroom above the trip threshold; headroom above the level
944
+ * risk-increasing actions must clear is `getEquityAboveBufferedFloor`.
945
+ */
946
+ getEquityAboveFloor() {
947
+ const equityFloor = this.getUserAccountOrThrow().equityFloor;
948
+ if (equityFloor.lte(numericConstants_1.ZERO)) {
949
+ return null;
950
+ }
951
+ return anchor_1.BN.max(this.getNetUsdValue().sub(equityFloor), numericConstants_1.ZERO);
952
+ }
953
+ /**
954
+ * Net equity (`getNetUsdValue`) in excess of `equityFloor +
955
+ * equityFloorBuffer`, floored at zero (QUOTE_PRECISION). Unbounded
956
+ * (`null`) when no floor is set. When this reaches zero, risk-increasing
957
+ * actions start rejecting.
920
958
  */
921
- getEquityAboveFloor(strict = false) {
959
+ getEquityAboveBufferedFloor() {
922
960
  const equityFloor = this.getUserAccountOrThrow().equityFloor;
923
961
  if (equityFloor.lte(numericConstants_1.ZERO)) {
924
962
  return null;
925
963
  }
926
- return anchor_1.BN.max(this.getTotalCollateral('Initial', strict).sub(equityFloor), numericConstants_1.ZERO);
964
+ return anchor_1.BN.max(this.getNetUsdValue().sub(this.getBufferedEquityFloor()), numericConstants_1.ZERO);
927
965
  }
928
966
  /**
929
967
  * Builds the liquidation-buffer map to pass into margin calculations while
@@ -1979,7 +2017,7 @@ class User {
1979
2017
  * @param outMarketIndex
1980
2018
  * @param calculateSwap Optional function to simulate the in-to-out conversion (e.g. to model swap fees/slippage); defaults to a 1:1 oracle-price conversion.
1981
2019
  * @param iterationLimit How many binary-search iterations to run before erroring out. Defaults to 1000.
1982
- * @returns `inAmount`/`outAmount` in each market's own token decimals, and the resulting `leverage` (TEN_THOUSAND, 1e4 precision) after the swap.
2020
+ * @returns `inAmount`/`outAmount` in each market's own token decimals, and the resulting `leverage` (TEN_THOUSAND, 1e4 precision) after the swap. Sizing is TWAP-bounded to match the program's margin check; `leverage` is marked at the live oracle price so it stays comparable to `getLeverage()`.
1983
2021
  */
1984
2022
  getMaxSwapAmount({ inMarketIndex, outMarketIndex, calculateSwap, iterationLimit = 1000, }) {
1985
2023
  const inMarket = this.velocityClient.getSpotMarketAccountOrThrow(inMarketIndex);
@@ -1988,8 +2026,14 @@ class User {
1988
2026
  const inOraclePrice = inOraclePriceData.price;
1989
2027
  const outOraclePriceData = this.getOracleDataForSpotMarket(outMarketIndex);
1990
2028
  const outOraclePrice = outOraclePriceData.price;
1991
- const inStrictOraclePrice = new strictOraclePrice_1.StrictOraclePrice(inOraclePrice);
1992
- const outStrictOraclePrice = new strictOraclePrice_1.StrictOraclePrice(outOraclePrice);
2029
+ // sizing mirrors handle_end_swap: both legs are priced strictly, against
2030
+ // each market's stored 5min TWAP
2031
+ const inStrictOraclePrice = new strictOraclePrice_1.StrictOraclePrice(inOraclePrice, inMarket.historicalOracleData.lastOraclePriceTwap5Min);
2032
+ const outStrictOraclePrice = new strictOraclePrice_1.StrictOraclePrice(outOraclePrice, outMarket.historicalOracleData.lastOraclePriceTwap5Min);
2033
+ // the returned leverage is a delta on top of getLeverageComponents' live-oracle
2034
+ // baseline, so pricing its legs strictly would mix two bases in one figure
2035
+ const inLeveragePrice = new strictOraclePrice_1.StrictOraclePrice(inOraclePrice);
2036
+ const outLeveragePrice = new strictOraclePrice_1.StrictOraclePrice(outOraclePrice);
1993
2037
  const inPrecision = new anchor_1.BN(10 ** inMarket.decimals);
1994
2038
  const outPrecision = new anchor_1.BN(10 ** outMarket.decimals);
1995
2039
  const inSpotPosition = this.getSpotPosition(inMarketIndex) ||
@@ -1998,9 +2042,9 @@ class User {
1998
2042
  this.getEmptySpotPosition(outMarketIndex);
1999
2043
  const freeCollateral = this.getFreeCollateral();
2000
2044
  const inContributionInitial = this.calculateSpotPositionFreeCollateralContribution(inSpotPosition, inStrictOraclePrice);
2001
- const { totalAssetValue: inTotalAssetValueInitial, totalLiabilityValue: inTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(inSpotPosition, inStrictOraclePrice);
2045
+ const { totalAssetValue: inTotalAssetValueInitial, totalLiabilityValue: inTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(inSpotPosition, inLeveragePrice);
2002
2046
  const outContributionInitial = this.calculateSpotPositionFreeCollateralContribution(outSpotPosition, outStrictOraclePrice);
2003
- const { totalAssetValue: outTotalAssetValueInitial, totalLiabilityValue: outTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(outSpotPosition, outStrictOraclePrice);
2047
+ const { totalAssetValue: outTotalAssetValueInitial, totalLiabilityValue: outTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(outSpotPosition, outLeveragePrice);
2004
2048
  const initialContribution = inContributionInitial.add(outContributionInitial);
2005
2049
  const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
2006
2050
  if (!calculateSwap) {
@@ -2063,8 +2107,8 @@ class User {
2063
2107
  }
2064
2108
  const inPositionAfter = this.cloneAndUpdateSpotPosition(inSpotPosition, inSwap.neg(), inMarket);
2065
2109
  const outPositionAfter = this.cloneAndUpdateSpotPosition(outSpotPosition, outSwap, outMarket);
2066
- const { totalAssetValue: inTotalAssetValueAfter, totalLiabilityValue: inTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(inPositionAfter, inStrictOraclePrice);
2067
- const { totalAssetValue: outTotalAssetValueAfter, totalLiabilityValue: outTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(outPositionAfter, outStrictOraclePrice);
2110
+ const { totalAssetValue: inTotalAssetValueAfter, totalLiabilityValue: inTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(inPositionAfter, inLeveragePrice);
2111
+ const { totalAssetValue: outTotalAssetValueAfter, totalLiabilityValue: outTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(outPositionAfter, outLeveragePrice);
2068
2112
  const spotAssetValueDelta = inTotalAssetValueAfter
2069
2113
  .add(outTotalAssetValueAfter)
2070
2114
  .sub(inTotalAssetValueInitial)
@@ -2147,7 +2191,8 @@ class User {
2147
2191
  };
2148
2192
  }
2149
2193
  /**
2150
- * Estimates what the user leverage will be after swap
2194
+ * Estimates what the user leverage will be after swap, marked at the live
2195
+ * oracle price so it stays comparable to `getLeverage()`.
2151
2196
  * @param inMarketIndex Market being sold/paid from.
2152
2197
  * @param outMarketIndex Market being bought/received.
2153
2198
  * @param inAmount Amount removed from `inMarketIndex`, that market's own token decimals.
@@ -2161,19 +2206,21 @@ class User {
2161
2206
  const inOraclePrice = inOraclePriceData.price;
2162
2207
  const outOraclePriceData = this.getOracleDataForSpotMarket(outMarketIndex);
2163
2208
  const outOraclePrice = outOraclePriceData.price;
2164
- const inStrictOraclePrice = new strictOraclePrice_1.StrictOraclePrice(inOraclePrice);
2165
- const outStrictOraclePrice = new strictOraclePrice_1.StrictOraclePrice(outOraclePrice);
2209
+ // same live-oracle basis as getMaxSwapAmount's leverage legs and as the
2210
+ // getLeverageComponents baseline these deltas are applied to
2211
+ const inLeveragePrice = new strictOraclePrice_1.StrictOraclePrice(inOraclePrice);
2212
+ const outLeveragePrice = new strictOraclePrice_1.StrictOraclePrice(outOraclePrice);
2166
2213
  const inSpotPosition = this.getSpotPosition(inMarketIndex) ||
2167
2214
  this.getEmptySpotPosition(inMarketIndex);
2168
2215
  const outSpotPosition = this.getSpotPosition(outMarketIndex) ||
2169
2216
  this.getEmptySpotPosition(outMarketIndex);
2170
- const { totalAssetValue: inTotalAssetValueInitial, totalLiabilityValue: inTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(inSpotPosition, inStrictOraclePrice);
2171
- const { totalAssetValue: outTotalAssetValueInitial, totalLiabilityValue: outTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(outSpotPosition, outStrictOraclePrice);
2217
+ const { totalAssetValue: inTotalAssetValueInitial, totalLiabilityValue: inTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(inSpotPosition, inLeveragePrice);
2218
+ const { totalAssetValue: outTotalAssetValueInitial, totalLiabilityValue: outTotalLiabilityValueInitial, } = this.calculateSpotPositionLeverageContribution(outSpotPosition, outLeveragePrice);
2172
2219
  const { perpLiabilityValue, perpPnl, spotAssetValue, spotLiabilityValue } = this.getLeverageComponents();
2173
2220
  const inPositionAfter = this.cloneAndUpdateSpotPosition(inSpotPosition, inAmount.abs().neg(), inMarket);
2174
2221
  const outPositionAfter = this.cloneAndUpdateSpotPosition(outSpotPosition, outAmount.abs(), outMarket);
2175
- const { totalAssetValue: inTotalAssetValueAfter, totalLiabilityValue: inTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(inPositionAfter, inStrictOraclePrice);
2176
- const { totalAssetValue: outTotalAssetValueAfter, totalLiabilityValue: outTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(outPositionAfter, outStrictOraclePrice);
2222
+ const { totalAssetValue: inTotalAssetValueAfter, totalLiabilityValue: inTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(inPositionAfter, inLeveragePrice);
2223
+ const { totalAssetValue: outTotalAssetValueAfter, totalLiabilityValue: outTotalLiabilityValueAfter, } = this.calculateSpotPositionLeverageContribution(outPositionAfter, outLeveragePrice);
2177
2224
  const spotAssetValueDelta = inTotalAssetValueAfter
2178
2225
  .add(outTotalAssetValueAfter)
2179
2226
  .sub(inTotalAssetValueInitial)
@@ -2380,9 +2427,9 @@ class User {
2380
2427
  const spotMarket = this.velocityClient.getSpotMarketAccountOrThrow(marketIndex);
2381
2428
  // eslint-disable-next-line prefer-const
2382
2429
  let { borrowLimit, withdrawLimit } = (0, spotBalance_2.calculateWithdrawLimit)(spotMarket, nowTs);
2383
- // the withdraw path enforces the equity floor on post-withdraw total
2384
- // collateral, so equity above the floor caps free collateral here
2385
- const equityAboveFloor = this.getEquityAboveFloor(true);
2430
+ // the withdraw path enforces the equity floor on post-withdraw net
2431
+ // equity, so equity above the floor caps free collateral here
2432
+ const equityAboveFloor = this.getEquityAboveFloor();
2386
2433
  if (equityAboveFloor !== null && equityAboveFloor.eq(numericConstants_1.ZERO)) {
2387
2434
  return numericConstants_1.ZERO;
2388
2435
  }
@@ -2921,8 +2968,10 @@ class User {
2921
2968
  skipTokenValue = true;
2922
2969
  }
2923
2970
  const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
2971
+ // mirrors margin.rs: strict mode prices against the market's *stored*
2972
+ // 5min TWAP, not a live-projected one
2924
2973
  const twap5 = strict
2925
- ? (0, oracles_1.calculateLiveOracleTwap)(spotMarket.historicalOracleData, oraclePriceData, new anchor_1.BN(Math.floor(Date.now() / 1000)), numericConstants_1.FIVE_MINUTE)
2974
+ ? spotMarket.historicalOracleData.lastOraclePriceTwap5Min
2926
2975
  : undefined;
2927
2976
  const strictOracle = new strictOraclePrice_1.StrictOraclePrice(oraclePriceData.price, twap5);
2928
2977
  if (isQuote) {
@@ -30,8 +30,8 @@ import { User } from './user';
30
30
  import { UserSubscriptionConfig } from './userConfig';
31
31
  import { VelocityEnv, VelocityProgram } from './config';
32
32
  import { UserStats } from './userStats';
33
- import { JupiterClient, QuoteResponse } from './jupiter/jupiterClient';
34
- import { SwapMode, UnifiedQuoteResponse } from './swap/UnifiedSwapClient';
33
+ import { JupiterClient, JupiterSwapQuote } from './jupiter/jupiterClient';
34
+ import { SwapMode, SwapProvider, SwapQuote } from './swap/types';
35
35
  import { UserStatsSubscriptionConfig } from './userStatsConfig';
36
36
  import { TxHandler } from './tx/txHandler';
37
37
  import { SignedMsgOrderParams } from './types';
@@ -39,7 +39,6 @@ import { TakerInfo } from './types';
39
39
  import { ConstituentMap } from './constituentMap/constituentMap';
40
40
  import { RevenueShareEscrowMap } from './userMap/revenueShareEscrowMap';
41
41
  import { TitanClient } from './titan/titanClient';
42
- import { UnifiedSwapClient } from './swap/UnifiedSwapClient';
43
42
  /**
44
43
  * Union type for swap clients (Titan and Jupiter) - Legacy type
45
44
  * @deprecated Use UnifiedSwapClient class instead
@@ -1573,16 +1572,20 @@ export declare class VelocityClient {
1573
1572
  * @param fromSubAccountId - Sub-account id to debit.
1574
1573
  * @param toSubAccountId - Sub-account id to credit.
1575
1574
  * @param equityFloorDelta - Equity floor (QUOTE_PRECISION) to move from the debited to the credited
1576
- * sub-account along with the funds, keeping the sum of floors constant. The debited side must not
1575
+ * sub-account along with the funds, keeping the sum of floors constant. A proportional share of
1576
+ * the debited side's `equityFloorBuffer` travels with the floor (rounded up on the debited side,
1577
+ * so shedding the whole floor also sheds the whole buffer; no orphan buffer is left on a
1578
+ * check-disabled sub-account), keeping the sum of buffers constant too. The debited side must not
1577
1579
  * already be below the floor being reduced (a below-floor sub-account cannot shed floor to defuse a
1578
- * pending equity-breaker trip), must stay at/above its reduced floor, and the credited side's
1579
- * collateral (after the transfer lands) must back its increased floor, else the transfer reverts with
1580
- * `InvalidEquityFloorTransfer`. Pass `'auto'`
1580
+ * pending equity-breaker trip), must stay at/above its reduced floor plus its reduced buffer,
1581
+ * and the credited side's net equity (after the transfer lands) must back its increased floor plus
1582
+ * its increased buffer, else the transfer reverts with `InvalidEquityFloorTransfer`. Pass `'auto'`
1581
1583
  * (quote market only) to move the minimal floor needed for the debited side to stay at/above its
1582
- * floor: `max(0, amount - (collateral - floor))`, capped at the debited side's floor. The auto delta
1583
- * never exceeds `amount`, so the credited side stays backed whenever it was before. Client-side
1584
- * pricing can differ slightly from the on-chain strict check at the exact boundary; retry with an
1585
- * explicit padded delta if an `'auto'` transfer reverts. Defaults to zero.
1584
+ * buffered floor: `max(0, amount - max(0, netEquity - (floor + buffer)))`, capped at the debited
1585
+ * side's floor (see `calculateEquityFloorAutoDelta`). The auto delta never exceeds `amount`, so the
1586
+ * credited side stays backed whenever it was before. Client-side pricing can differ slightly from
1587
+ * the onchain check at the exact boundary; retry with an explicit padded delta if an
1588
+ * `'auto'` transfer reverts. Defaults to zero.
1586
1589
  * @param txParams - Optional compute-unit/priority-fee overrides for the transaction.
1587
1590
  * @returns The transaction signature.
1588
1591
  * @throws (on-chain) if `allowDelegateTransfer` is not enabled, if the signer is not the delegate on
@@ -2304,34 +2307,32 @@ export declare class VelocityClient {
2304
2307
  * instructions in a single transaction, so the swap is settled directly against the user's
2305
2308
  * deposits/vault balances rather than the wallet's own token accounts. Sends and confirms the
2306
2309
  * transaction.
2307
- * @param swapClient - Swap client used to fetch routes/instructions (`UnifiedSwapClient` or a
2308
- * `TitanClient`); dispatches to `getSwapIxV2` or `getTitanSwapIx` respectively.
2309
- * @param jupiterClient - @deprecated Use `swapClient` instead. When passed (and `swapClient` is
2310
- * not), dispatches to `getJupiterSwapIxV6`.
2310
+ * @param swapClient - Provider used to quote the swap and build its route: a
2311
+ * `UnifiedSwapClient`, or a `TitanClient`/`JupiterClient` directly. See `getProviderSwapIx`.
2312
+ * @param jupiterClient - @deprecated Use `swapClient` instead. Used only when `swapClient` is
2313
+ * not passed.
2311
2314
  * @param outMarketIndex - Spot market index of the token being bought.
2312
2315
  * @param inMarketIndex - Spot market index of the token being sold.
2313
2316
  * @param outAssociatedTokenAccount - Token account to receive the bought token; created
2314
2317
  * idempotently if omitted.
2315
2318
  * @param inAssociatedTokenAccount - Token account to source the sold token from; created
2316
2319
  * idempotently if omitted.
2317
- * @param amount - Amount of the "in" token (or "out" token when `swapMode` is `ExactOut`, in
2318
- * which case this is the desired output amount), in the token's own mint decimals — not a
2319
- * fixed protocol precision.
2320
+ * @param amount - Amount of the "in" token (or "out" token when the effective mode is
2321
+ * `ExactOut`, in which case this is the desired output amount), in the token's own mint
2322
+ * decimals — not a fixed protocol precision.
2320
2323
  * @param slippageBps - Max slippage in basis points passed to the swap provider's routing API.
2321
- * @param swapMode - `ExactIn` (default) or `ExactOut`.
2324
+ * @param swapMode - `ExactIn` (default) or `ExactOut`. Ignored when `quote` is passed — the
2325
+ * quote's own mode wins.
2322
2326
  * @param reduceOnly - Whether the in/out token's position on the velocity account must reduce
2323
2327
  * (not flip sign); enforced by `endSwap` after the swap completes.
2324
- * @param v6 - @deprecated Use `quote` instead. Pre-fetched Jupiter v6 quote response.
2325
- * @param quote - Pre-fetched quote response (skips an extra round-trip to the swap provider).
2328
+ * @param quote - Pre-fetched quote (skips an extra round-trip to the swap provider). Must be
2329
+ * for this pair and this `amount`.
2326
2330
  * @param txParams - Optional compute-unit/priority-fee overrides.
2327
- * @throws If neither `swapClient` nor `jupiterClient` is provided, or if `swapClient` is not a
2328
- * recognized client type.
2331
+ * @throws If neither `swapClient` nor `jupiterClient` is provided.
2329
2332
  * @returns The transaction signature.
2330
2333
  */
2331
- swap({ swapClient, jupiterClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, reduceOnly, txParams, v6, quote, onlyDirectRoutes, }: {
2332
- swapClient?: UnifiedSwapClient | SwapClient;
2333
- /** @deprecated Use swapClient instead. Legacy parameter for backward compatibility */
2334
- jupiterClient?: JupiterClient;
2334
+ swap({ swapClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, reduceOnly, txParams, quote, onlyDirectRoutes, }: {
2335
+ swapClient?: SwapProvider;
2335
2336
  outMarketIndex: number;
2336
2337
  inMarketIndex: number;
2337
2338
  outAssociatedTokenAccount?: PublicKey;
@@ -2342,49 +2343,59 @@ export declare class VelocityClient {
2342
2343
  reduceOnly?: SwapReduceOnly;
2343
2344
  txParams?: TxParams;
2344
2345
  onlyDirectRoutes?: boolean;
2345
- v6?: {
2346
- quote?: QuoteResponse;
2347
- };
2348
- quote?: UnifiedQuoteResponse;
2346
+ quote?: SwapQuote;
2349
2347
  }): Promise<TransactionSignature>;
2350
2348
  /**
2351
- * Builds the instruction list for a Titan-routed swap: creates any missing associated token
2352
- * accounts, wraps Titan's routing instructions between `beginSwap`/`endSwap`. See `swap` for
2353
- * parameter semantics; `amount` is in the "in" token's mint decimals.
2354
- * @param userAccountPublicKey - Optional user account override (e.g. when the account is being
2355
- * created in the same transaction and not yet resolvable via `getUserAccountPublicKey`).
2356
- * @returns `ixs` — instruction list (ATA creation, `beginSwap`, Titan swap instructions,
2357
- * `endSwap`, in order) and `lookupTables` needed to fit it in a versioned transaction.
2349
+ * Throws unless a quote swaps exactly the pair the `beginSwap`/`endSwap` pair is being built
2350
+ * for. A mismatched quote routes and executes normally, but deposits its output into a token
2351
+ * account `endSwap` isn't watching, so it reverts with `InvalidSwap: amount_out must be
2352
+ * greater than 0` only after the funds have already moved.
2358
2353
  */
2359
- getTitanSwapIx({ titanClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, onlyDirectRoutes, reduceOnly, userAccountPublicKey, }: {
2360
- titanClient: TitanClient;
2361
- outMarketIndex: number;
2362
- inMarketIndex: number;
2363
- outAssociatedTokenAccount?: PublicKey;
2364
- inAssociatedTokenAccount?: PublicKey;
2365
- amount: BN;
2366
- slippageBps?: number;
2367
- swapMode?: string;
2368
- onlyDirectRoutes?: boolean;
2369
- reduceOnly?: SwapReduceOnly;
2370
- userAccountPublicKey?: PublicKey;
2371
- }): Promise<{
2372
- ixs: TransactionInstruction[];
2373
- lookupTables: AddressLookupTableAccount[];
2374
- }>;
2354
+ protected assertQuoteMatchesMarkets(quote: {
2355
+ inputMint: string;
2356
+ outputMint: string;
2357
+ }, inMarket: SpotMarketAccount, outMarket: SpotMarketAccount): void;
2375
2358
  /**
2376
- * Builds the instruction list for a Jupiter v6-routed swap: fetches a quote if none is passed,
2377
- * creates any missing associated token accounts, and wraps Jupiter's routing instructions
2378
- * between `beginSwap`/`endSwap`. See `swap` for parameter semantics; `amount` is in the "in"
2379
- * token's mint decimals.
2359
+ * Throws unless a quote is for the size the caller asked to swap. `beginSwap` releases funds
2360
+ * sized off the quote, so a quote for a different size moves the wrong amount out of the user's
2361
+ * deposits.
2362
+ */
2363
+ private assertQuoteMatchesAmount;
2364
+ /**
2365
+ * Resolves the wallet's associated token account for a spot market, plus the instruction that
2366
+ * creates it when it doesn't exist yet.
2367
+ */
2368
+ private getOrCreateSwapTokenAccount;
2369
+ /**
2370
+ * Builds the instruction list for a swap routed through any `SwapProvider` (Jupiter, Titan, or
2371
+ * a `UnifiedSwapClient` wrapping either): creates any missing associated token accounts and
2372
+ * wraps the provider's routing instructions between `beginSwap`/`endSwap`.
2373
+ * @param swapProvider - Provider that quotes the swap and builds its route instructions.
2374
+ * @param outMarketIndex - Spot market index of the token being bought.
2375
+ * @param inMarketIndex - Spot market index of the token being sold.
2376
+ * @param outAssociatedTokenAccount - Token account to receive the bought token; created
2377
+ * idempotently if omitted.
2378
+ * @param inAssociatedTokenAccount - Token account to source the sold token from; created
2379
+ * idempotently if omitted.
2380
+ * @param amount - Amount in the "in" token's mint decimals, or the "out" token's when the
2381
+ * effective mode is `ExactOut`.
2382
+ * @param slippageBps - Max slippage in basis points; only used when a quote has to be fetched.
2383
+ * @param swapMode - `ExactIn` (default) or `ExactOut`. The mode a quote is fetched at; the
2384
+ * resulting quote's own mode is what sizes the swap, so it is ignored when `quote` is passed.
2385
+ * @param onlyDirectRoutes - Restricts a fetched quote to single-hop routes.
2386
+ * @param maxAccounts - Account budget for a fetched route.
2387
+ * @param reduceOnly - Which side must not increase in magnitude; enforced by `endSwap`.
2388
+ * @param quote - Pre-fetched quote. Authoritative when passed: its `swapMode` is the effective
2389
+ * mode, and it must be for this pair and this `amount`.
2380
2390
  * @param userAccountPublicKey - Optional user account override (e.g. when the account is being
2381
- * created in the same transaction).
2382
- * @throws If no quote is passed and Jupiter's quote API returns none.
2383
- * @returns `ixs` instruction list (ATA creation, `beginSwap`, Jupiter swap instructions,
2384
- * `endSwap`, in order) and `lookupTables` needed to fit it in a versioned transaction.
2391
+ * created in the same transaction and not yet resolvable via `getUserAccountPublicKey`).
2392
+ * @throws If the quote passed in or freshly fetched is for a different pair or a different
2393
+ * size than the swap being built.
2394
+ * @returns `ixs` ATA creation, `beginSwap`, the route's instructions, `endSwap`, in order
2395
+ * and the `lookupTables` needed to fit them in a versioned transaction.
2385
2396
  */
2386
- getJupiterSwapIxV6({ jupiterClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, onlyDirectRoutes, quote, reduceOnly, userAccountPublicKey, }: {
2387
- jupiterClient: JupiterClient;
2397
+ getProviderSwapIx({ swapProvider, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, onlyDirectRoutes, maxAccounts, reduceOnly, quote, userAccountPublicKey, }: {
2398
+ swapProvider: SwapProvider;
2388
2399
  outMarketIndex: number;
2389
2400
  inMarketIndex: number;
2390
2401
  outAssociatedTokenAccount?: PublicKey;
@@ -2393,8 +2404,9 @@ export declare class VelocityClient {
2393
2404
  slippageBps?: number;
2394
2405
  swapMode?: SwapMode;
2395
2406
  onlyDirectRoutes?: boolean;
2396
- quote?: QuoteResponse;
2407
+ maxAccounts?: number;
2397
2408
  reduceOnly?: SwapReduceOnly;
2409
+ quote?: SwapQuote;
2398
2410
  userAccountPublicKey?: PublicKey;
2399
2411
  }): Promise<{
2400
2412
  ixs: TransactionInstruction[];
@@ -2434,37 +2446,6 @@ export declare class VelocityClient {
2434
2446
  beginSwapIx: TransactionInstruction;
2435
2447
  endSwapIx: TransactionInstruction;
2436
2448
  }>;
2437
- /**
2438
- * Builds the instruction list for a swap routed through a `UnifiedSwapClient` (the current
2439
- * preferred swap path). Creates any missing associated token accounts and wraps the client's
2440
- * routing instructions between `beginSwap`/`endSwap`. See `swap` for parameter semantics;
2441
- * `amount` is in the "in" token's mint decimals (or "out" token's decimals when `swapMode` is
2442
- * `ExactOut`).
2443
- * @param userAccountPublicKey - Optional user account override (e.g. when the account is being
2444
- * created in the same transaction).
2445
- * @returns `ixs` — instruction list (ATA creation, `beginSwap`, routed swap instructions,
2446
- * `endSwap`, in order) and `lookupTables` needed to fit it in a versioned transaction.
2447
- */
2448
- getSwapIxV2({ swapClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, onlyDirectRoutes, reduceOnly, quote, v6, userAccountPublicKey, }: {
2449
- swapClient: UnifiedSwapClient;
2450
- outMarketIndex: number;
2451
- inMarketIndex: number;
2452
- outAssociatedTokenAccount?: PublicKey;
2453
- inAssociatedTokenAccount?: PublicKey;
2454
- amount: BN;
2455
- slippageBps?: number;
2456
- swapMode?: SwapMode;
2457
- onlyDirectRoutes?: boolean;
2458
- reduceOnly?: SwapReduceOnly;
2459
- quote?: UnifiedQuoteResponse;
2460
- v6?: {
2461
- quote?: QuoteResponse;
2462
- };
2463
- userAccountPublicKey?: PublicKey;
2464
- }): Promise<{
2465
- ixs: TransactionInstruction[];
2466
- lookupTables: AddressLookupTableAccount[];
2467
- }>;
2468
2449
  /**
2469
2450
  * Converts a portion of the user's deposited wSOL (spot market index 1) into mSOL (spot market
2470
2451
  * index 2) by staking it with Marinade Finance, then swapping the resulting mSOL back into the
@@ -2533,11 +2514,12 @@ export declare class VelocityClient {
2533
2514
  getForceCancelOrdersIx(userAccountPublicKey: PublicKey, userAccount: UserAccount, fillerPublicKey?: PublicKey): Promise<TransactionInstruction>;
2534
2515
  /**
2535
2516
  * Keeper instruction: trips the authority-wide equity floor breaker. Proves on-chain that the
2536
- * given subaccount's cross-margin total collateral is below its `equityFloor` (reverts with
2537
- * `SufficientCollateral` otherwise, or if no floor is set) and sets `equityBreakerTripped` on the
2538
- * authority's `UserStats` every subaccount of the authority then rejects risk-increasing fills,
2539
- * withdrawals and transfers out until the warm admin calls `resetEquityFloorBreaker`.
2540
- * Permissionless any signer may trip it; the margin calculation is the proof.
2517
+ * given subaccount's net equity (unweighted assets and perp PnL minus spot liabilities) is below
2518
+ * its `equityFloor` (reverts with `SufficientCollateral` otherwise, if no floor is set, or with
2519
+ * `InvalidOracle` if any of the subaccount's oracles is invalid) and sets `equityBreakerTripped`
2520
+ * on the authority's `UserStats` every subaccount of the authority then rejects risk-increasing
2521
+ * fills, withdrawals and transfers out until the warm admin calls `resetEquityFloorBreaker`.
2522
+ * Permissionless — any signer may trip it; the equity calculation is the proof.
2541
2523
  * @param userAccountPublicKey - Public key of the breached subaccount's user account.
2542
2524
  * @param user - Decoded user account of the breached subaccount.
2543
2525
  * @param txParams - Optional compute-unit/priority-fee overrides.
@@ -3325,7 +3307,8 @@ export declare class VelocityClient {
3325
3307
  * @param userAccountPublicKey - Public key of the user account being liquidated.
3326
3308
  * @param liquidatorSubAccountId - Liquidator's sub-account to credit; defaults to the active sub-account.
3327
3309
  * @param maxAccounts - Caps the number of accounts Jupiter's route may use.
3328
- * @throws If no quote can be fetched and `quote` was not supplied.
3310
+ * @throws If no quote can be fetched and `quote` was not supplied, or if the quote — passed in or
3311
+ * freshly fetched — is for a different pair or a different size than the swap being built.
3329
3312
  * @returns The ordered instructions (pre-instructions, `beginSwap`, Jupiter swap, `endSwap`) and
3330
3313
  * any address lookup tables the Jupiter route requires.
3331
3314
  */
@@ -3339,7 +3322,7 @@ export declare class VelocityClient {
3339
3322
  slippageBps?: number;
3340
3323
  swapMode?: SwapMode;
3341
3324
  onlyDirectRoutes?: boolean;
3342
- quote?: QuoteResponse;
3325
+ quote?: JupiterSwapQuote;
3343
3326
  userAccount: UserAccount;
3344
3327
  userAccountPublicKey: PublicKey;
3345
3328
  liquidatorSubAccountId?: number;