@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
@@ -19,6 +19,7 @@ exports.VelocityClient = void 0;
19
19
  * Instruction → on-chain handler mapping: see ARCHITECTURE.md § SDK↔Instruction Mapping.
20
20
  */
21
21
  const anchor_1 = require("./isomorphic/anchor");
22
+ const margin_1 = require("./math/margin");
22
23
  const bs58_1 = __importDefault(require("bs58"));
23
24
  const spl_token_1 = require("@solana/spl-token");
24
25
  const types_1 = require("./types");
@@ -44,7 +45,7 @@ const spotPosition_1 = require("./math/spotPosition");
44
45
  const market_1 = require("./math/market");
45
46
  const fetch_1 = require("./accounts/fetch");
46
47
  const spotMarket_1 = require("./math/spotMarket");
47
- const jupiterClient_1 = require("./jupiter/jupiterClient");
48
+ const UnifiedSwapClient_1 = require("./swap/UnifiedSwapClient");
48
49
  const memcmp_1 = require("./memcmp");
49
50
  const marinade_1 = require("./marinade");
50
51
  const orderParams_1 = require("./orderParams");
@@ -60,8 +61,6 @@ const oracleId_1 = require("./oracles/oracleId");
60
61
  const utils_3 = require("./oracles/utils");
61
62
  const orders_1 = require("./math/orders");
62
63
  const builder_1 = require("./math/builder");
63
- const titanClient_1 = require("./titan/titanClient");
64
- const UnifiedSwapClient_1 = require("./swap/UnifiedSwapClient");
65
64
  /**
66
65
  * # VelocityClient
67
66
  * Main entry point for interacting with Velocity Exchange from TypeScript. A single instance wraps
@@ -3154,16 +3153,20 @@ class VelocityClient {
3154
3153
  * @param fromSubAccountId - Sub-account id to debit.
3155
3154
  * @param toSubAccountId - Sub-account id to credit.
3156
3155
  * @param equityFloorDelta - Equity floor (QUOTE_PRECISION) to move from the debited to the credited
3157
- * sub-account along with the funds, keeping the sum of floors constant. The debited side must not
3156
+ * sub-account along with the funds, keeping the sum of floors constant. A proportional share of
3157
+ * the debited side's `equityFloorBuffer` travels with the floor (rounded up on the debited side,
3158
+ * so shedding the whole floor also sheds the whole buffer; no orphan buffer is left on a
3159
+ * check-disabled sub-account), keeping the sum of buffers constant too. The debited side must not
3158
3160
  * already be below the floor being reduced (a below-floor sub-account cannot shed floor to defuse a
3159
- * pending equity-breaker trip), must stay at/above its reduced floor, and the credited side's
3160
- * collateral (after the transfer lands) must back its increased floor, else the transfer reverts with
3161
- * `InvalidEquityFloorTransfer`. Pass `'auto'`
3161
+ * pending equity-breaker trip), must stay at/above its reduced floor plus its reduced buffer,
3162
+ * and the credited side's net equity (after the transfer lands) must back its increased floor plus
3163
+ * its increased buffer, else the transfer reverts with `InvalidEquityFloorTransfer`. Pass `'auto'`
3162
3164
  * (quote market only) to move the minimal floor needed for the debited side to stay at/above its
3163
- * floor: `max(0, amount - (collateral - floor))`, capped at the debited side's floor. The auto delta
3164
- * never exceeds `amount`, so the credited side stays backed whenever it was before. Client-side
3165
- * pricing can differ slightly from the on-chain strict check at the exact boundary; retry with an
3166
- * explicit padded delta if an `'auto'` transfer reverts. Defaults to zero.
3165
+ * buffered floor: `max(0, amount - max(0, netEquity - (floor + buffer)))`, capped at the debited
3166
+ * side's floor (see `calculateEquityFloorAutoDelta`). The auto delta never exceeds `amount`, so the
3167
+ * credited side stays backed whenever it was before. Client-side pricing can differ slightly from
3168
+ * the onchain check at the exact boundary; retry with an explicit padded delta if an
3169
+ * `'auto'` transfer reverts. Defaults to zero.
3167
3170
  * @param txParams - Optional compute-unit/priority-fee overrides for the transaction.
3168
3171
  * @returns The transaction signature.
3169
3172
  * @throws (on-chain) if `allowDelegateTransfer` is not enabled, if the signer is not the delegate on
@@ -3200,14 +3203,8 @@ class VelocityClient {
3200
3203
  throw new Error("equityFloorDelta 'auto' is only supported for the quote spot market; pass an explicit delta");
3201
3204
  }
3202
3205
  const fromUserClass = this.getUser(fromSubAccountId, this.authority);
3203
- const floor = fromUserClass.getUserAccountOrThrow().equityFloor;
3204
- if (floor.lte(numericConstants_1.ZERO)) {
3205
- resolvedFloorDelta = numericConstants_1.ZERO;
3206
- }
3207
- else {
3208
- const excess = anchor_1.BN.max(fromUserClass.getTotalCollateral('Initial', true).sub(floor), numericConstants_1.ZERO);
3209
- resolvedFloorDelta = anchor_1.BN.min(anchor_1.BN.max(amount.sub(excess), numericConstants_1.ZERO), floor);
3210
- }
3206
+ const fromUserAccount = fromUserClass.getUserAccountOrThrow();
3207
+ resolvedFloorDelta = (0, margin_1.calculateEquityFloorAutoDelta)(amount, fromUserClass.getNetUsdValue(), fromUserAccount.equityFloor, fromUserAccount.equityFloorBuffer);
3211
3208
  }
3212
3209
  else {
3213
3210
  resolvedFloorDelta = equityFloorDelta;
@@ -4721,234 +4718,182 @@ class VelocityClient {
4721
4718
  * instructions in a single transaction, so the swap is settled directly against the user's
4722
4719
  * deposits/vault balances rather than the wallet's own token accounts. Sends and confirms the
4723
4720
  * transaction.
4724
- * @param swapClient - Swap client used to fetch routes/instructions (`UnifiedSwapClient` or a
4725
- * `TitanClient`); dispatches to `getSwapIxV2` or `getTitanSwapIx` respectively.
4726
- * @param jupiterClient - @deprecated Use `swapClient` instead. When passed (and `swapClient` is
4727
- * not), dispatches to `getJupiterSwapIxV6`.
4721
+ * @param swapClient - Provider used to quote the swap and build its route: a
4722
+ * `UnifiedSwapClient`, or a `TitanClient`/`JupiterClient` directly. See `getProviderSwapIx`.
4723
+ * @param jupiterClient - @deprecated Use `swapClient` instead. Used only when `swapClient` is
4724
+ * not passed.
4728
4725
  * @param outMarketIndex - Spot market index of the token being bought.
4729
4726
  * @param inMarketIndex - Spot market index of the token being sold.
4730
4727
  * @param outAssociatedTokenAccount - Token account to receive the bought token; created
4731
4728
  * idempotently if omitted.
4732
4729
  * @param inAssociatedTokenAccount - Token account to source the sold token from; created
4733
4730
  * idempotently if omitted.
4734
- * @param amount - Amount of the "in" token (or "out" token when `swapMode` is `ExactOut`, in
4735
- * which case this is the desired output amount), in the token's own mint decimals — not a
4736
- * fixed protocol precision.
4731
+ * @param amount - Amount of the "in" token (or "out" token when the effective mode is
4732
+ * `ExactOut`, in which case this is the desired output amount), in the token's own mint
4733
+ * decimals — not a fixed protocol precision.
4737
4734
  * @param slippageBps - Max slippage in basis points passed to the swap provider's routing API.
4738
- * @param swapMode - `ExactIn` (default) or `ExactOut`.
4735
+ * @param swapMode - `ExactIn` (default) or `ExactOut`. Ignored when `quote` is passed — the
4736
+ * quote's own mode wins.
4739
4737
  * @param reduceOnly - Whether the in/out token's position on the velocity account must reduce
4740
4738
  * (not flip sign); enforced by `endSwap` after the swap completes.
4741
- * @param v6 - @deprecated Use `quote` instead. Pre-fetched Jupiter v6 quote response.
4742
- * @param quote - Pre-fetched quote response (skips an extra round-trip to the swap provider).
4739
+ * @param quote - Pre-fetched quote (skips an extra round-trip to the swap provider). Must be
4740
+ * for this pair and this `amount`.
4743
4741
  * @param txParams - Optional compute-unit/priority-fee overrides.
4744
- * @throws If neither `swapClient` nor `jupiterClient` is provided, or if `swapClient` is not a
4745
- * recognized client type.
4742
+ * @throws If neither `swapClient` nor `jupiterClient` is provided.
4746
4743
  * @returns The transaction signature.
4747
4744
  */
4748
- async swap({ swapClient, jupiterClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, reduceOnly, txParams, v6, quote, onlyDirectRoutes = false, }) {
4749
- // Handle backward compatibility: use jupiterClient if swapClient is not provided
4750
- const clientToUse = swapClient || jupiterClient;
4751
- if (!clientToUse) {
4745
+ async swap({ swapClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, reduceOnly, txParams, quote, onlyDirectRoutes = false, }) {
4746
+ if (!swapClient) {
4752
4747
  throw new Error('Either swapClient or jupiterClient must be provided');
4753
4748
  }
4754
- let res;
4755
- // Use unified SwapClient if available
4756
- if (clientToUse instanceof UnifiedSwapClient_1.UnifiedSwapClient) {
4757
- res = await this.getSwapIxV2({
4758
- swapClient: clientToUse,
4759
- outMarketIndex,
4760
- inMarketIndex,
4761
- outAssociatedTokenAccount,
4762
- inAssociatedTokenAccount,
4763
- amount,
4764
- slippageBps,
4765
- swapMode,
4766
- onlyDirectRoutes,
4767
- reduceOnly,
4768
- quote,
4769
- v6,
4770
- });
4771
- }
4772
- else if (clientToUse instanceof titanClient_1.TitanClient) {
4773
- res = await this.getTitanSwapIx({
4774
- titanClient: clientToUse,
4775
- outMarketIndex,
4776
- inMarketIndex,
4777
- outAssociatedTokenAccount,
4778
- inAssociatedTokenAccount,
4779
- amount,
4780
- slippageBps,
4781
- swapMode,
4782
- onlyDirectRoutes,
4783
- reduceOnly,
4784
- });
4785
- }
4786
- else if (clientToUse instanceof jupiterClient_1.JupiterClient) {
4787
- const quoteToUse = quote !== null && quote !== void 0 ? quote : v6 === null || v6 === void 0 ? void 0 : v6.quote;
4788
- res = await this.getJupiterSwapIxV6({
4789
- jupiterClient: clientToUse,
4790
- outMarketIndex,
4791
- inMarketIndex,
4792
- outAssociatedTokenAccount,
4793
- inAssociatedTokenAccount,
4794
- amount,
4795
- slippageBps,
4796
- swapMode,
4797
- quote: quoteToUse,
4798
- reduceOnly,
4799
- onlyDirectRoutes,
4800
- });
4801
- }
4802
- else {
4803
- throw new Error('Invalid swap client type. Must be SwapClient, TitanClient, or JupiterClient.');
4804
- }
4805
- const ixs = res.ixs;
4806
- const lookupTables = res.lookupTables;
4749
+ const { ixs, lookupTables } = await this.getProviderSwapIx({
4750
+ swapProvider: swapClient,
4751
+ outMarketIndex,
4752
+ inMarketIndex,
4753
+ outAssociatedTokenAccount,
4754
+ inAssociatedTokenAccount,
4755
+ amount,
4756
+ slippageBps,
4757
+ swapMode,
4758
+ onlyDirectRoutes,
4759
+ reduceOnly,
4760
+ quote,
4761
+ });
4807
4762
  const tx = (await this.buildTransaction(ixs, txParams, 0, lookupTables));
4808
4763
  const { txSig, slot } = await this.sendTransaction(tx);
4809
4764
  this.cacheSpotMarketSlot(slot, outMarketIndex, inMarketIndex);
4810
4765
  return txSig;
4811
4766
  }
4812
4767
  /**
4813
- * Builds the instruction list for a Titan-routed swap: creates any missing associated token
4814
- * accounts, wraps Titan's routing instructions between `beginSwap`/`endSwap`. See `swap` for
4815
- * parameter semantics; `amount` is in the "in" token's mint decimals.
4816
- * @param userAccountPublicKey - Optional user account override (e.g. when the account is being
4817
- * created in the same transaction and not yet resolvable via `getUserAccountPublicKey`).
4818
- * @returns `ixs` — instruction list (ATA creation, `beginSwap`, Titan swap instructions,
4819
- * `endSwap`, in order) and `lookupTables` needed to fit it in a versioned transaction.
4768
+ * Throws unless a quote swaps exactly the pair the `beginSwap`/`endSwap` pair is being built
4769
+ * for. A mismatched quote routes and executes normally, but deposits its output into a token
4770
+ * account `endSwap` isn't watching, so it reverts with `InvalidSwap: amount_out must be
4771
+ * greater than 0` only after the funds have already moved.
4820
4772
  */
4821
- async getTitanSwapIx({ titanClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, onlyDirectRoutes, reduceOnly, userAccountPublicKey, }) {
4822
- const outMarket = this.getSpotMarketAccountOrThrow(outMarketIndex);
4823
- const inMarket = this.getSpotMarketAccountOrThrow(inMarketIndex);
4773
+ assertQuoteMatchesMarkets(quote, inMarket, outMarket) {
4774
+ if (!new web3_js_1.PublicKey(quote.inputMint).equals(inMarket.mint)) {
4775
+ throw new Error(`Quote sells ${quote.inputMint} but spot market ${inMarket.marketIndex} is ${inMarket.mint.toString()}.`);
4776
+ }
4777
+ if (!new web3_js_1.PublicKey(quote.outputMint).equals(outMarket.mint)) {
4778
+ throw new Error(`Quote buys ${quote.outputMint} but spot market ${outMarket.marketIndex} is ${outMarket.mint.toString()}.`);
4779
+ }
4780
+ }
4781
+ /**
4782
+ * Throws unless a quote is for the size the caller asked to swap. `beginSwap` releases funds
4783
+ * sized off the quote, so a quote for a different size moves the wrong amount out of the user's
4784
+ * deposits.
4785
+ */
4786
+ assertQuoteMatchesAmount(quote, amount, swapMode) {
4824
4787
  const isExactOut = swapMode === 'ExactOut';
4825
- const exactOutBufferedAmountIn = amount.muln(1001).divn(1000); // Add 10bp buffer
4826
- const preInstructions = [];
4827
- if (!outAssociatedTokenAccount) {
4828
- const tokenProgram = this.getTokenProgramForSpotMarket(outMarket);
4829
- outAssociatedTokenAccount = await this.getAssociatedTokenAccount(outMarket.marketIndex, false, tokenProgram);
4830
- const accountInfo = await this.connection.getAccountInfo(outAssociatedTokenAccount);
4831
- if (!accountInfo) {
4832
- preInstructions.push(this.createAssociatedTokenAccountIdempotentInstruction(outAssociatedTokenAccount, this.provider.wallet.publicKey, this.provider.wallet.publicKey, outMarket.mint, tokenProgram));
4833
- }
4788
+ const quoted = isExactOut ? quote.outAmount : quote.inAmount;
4789
+ if (quoted !== amount.toString()) {
4790
+ throw new Error(`Quote is for ${quoted} ${isExactOut ? 'out' : 'in'} but the swap asked for ${amount.toString()} (${swapMode}).`);
4834
4791
  }
4835
- if (!inAssociatedTokenAccount) {
4836
- const tokenProgram = this.getTokenProgramForSpotMarket(inMarket);
4837
- inAssociatedTokenAccount = await this.getAssociatedTokenAccount(inMarket.marketIndex, false, tokenProgram);
4838
- const accountInfo = await this.connection.getAccountInfo(inAssociatedTokenAccount);
4839
- if (!accountInfo) {
4840
- preInstructions.push(this.createAssociatedTokenAccountIdempotentInstruction(inAssociatedTokenAccount, this.provider.wallet.publicKey, this.provider.wallet.publicKey, inMarket.mint, tokenProgram));
4841
- }
4792
+ }
4793
+ /**
4794
+ * Resolves the wallet's associated token account for a spot market, plus the instruction that
4795
+ * creates it when it doesn't exist yet.
4796
+ */
4797
+ async getOrCreateSwapTokenAccount(market) {
4798
+ const tokenProgram = this.getTokenProgramForSpotMarket(market);
4799
+ const tokenAccount = await this.getAssociatedTokenAccount(market.marketIndex, false, tokenProgram);
4800
+ if (await this.connection.getAccountInfo(tokenAccount)) {
4801
+ return { tokenAccount };
4842
4802
  }
4843
- const { beginSwapIx, endSwapIx } = await this.getSwapIx({
4844
- outMarketIndex,
4845
- inMarketIndex,
4846
- amountIn: isExactOut ? exactOutBufferedAmountIn : amount,
4847
- inTokenAccount: inAssociatedTokenAccount,
4848
- outTokenAccount: outAssociatedTokenAccount,
4849
- reduceOnly,
4850
- userAccountPublicKey,
4851
- });
4852
- const { transactionMessage, lookupTables } = await titanClient.getSwap({
4853
- inputMint: inMarket.mint,
4854
- outputMint: outMarket.mint,
4855
- amount,
4856
- userPublicKey: this.provider.wallet.publicKey,
4857
- slippageBps,
4858
- swapMode: isExactOut ? titanClient_1.SwapMode.ExactOut : titanClient_1.SwapMode.ExactIn,
4859
- onlyDirectRoutes,
4860
- sizeConstraint: utils_2.MAX_TX_BYTE_SIZE - 375, // buffer for velocity instructions
4861
- });
4862
- const titanInstructions = titanClient.getTitanInstructions({
4863
- transactionMessage,
4864
- inputMint: inMarket.mint,
4865
- outputMint: outMarket.mint,
4866
- });
4867
- const ixs = [
4868
- ...preInstructions,
4869
- beginSwapIx,
4870
- ...titanInstructions,
4871
- endSwapIx,
4872
- ];
4873
- return { ixs, lookupTables };
4803
+ return {
4804
+ tokenAccount,
4805
+ createIx: this.createAssociatedTokenAccountIdempotentInstruction(tokenAccount, this.provider.wallet.publicKey, this.provider.wallet.publicKey, market.mint, tokenProgram),
4806
+ };
4874
4807
  }
4875
4808
  /**
4876
- * Builds the instruction list for a Jupiter v6-routed swap: fetches a quote if none is passed,
4877
- * creates any missing associated token accounts, and wraps Jupiter's routing instructions
4878
- * between `beginSwap`/`endSwap`. See `swap` for parameter semantics; `amount` is in the "in"
4879
- * token's mint decimals.
4809
+ * Builds the instruction list for a swap routed through any `SwapProvider` (Jupiter, Titan, or
4810
+ * a `UnifiedSwapClient` wrapping either): creates any missing associated token accounts and
4811
+ * wraps the provider's routing instructions between `beginSwap`/`endSwap`.
4812
+ * @param swapProvider - Provider that quotes the swap and builds its route instructions.
4813
+ * @param outMarketIndex - Spot market index of the token being bought.
4814
+ * @param inMarketIndex - Spot market index of the token being sold.
4815
+ * @param outAssociatedTokenAccount - Token account to receive the bought token; created
4816
+ * idempotently if omitted.
4817
+ * @param inAssociatedTokenAccount - Token account to source the sold token from; created
4818
+ * idempotently if omitted.
4819
+ * @param amount - Amount in the "in" token's mint decimals, or the "out" token's when the
4820
+ * effective mode is `ExactOut`.
4821
+ * @param slippageBps - Max slippage in basis points; only used when a quote has to be fetched.
4822
+ * @param swapMode - `ExactIn` (default) or `ExactOut`. The mode a quote is fetched at; the
4823
+ * resulting quote's own mode is what sizes the swap, so it is ignored when `quote` is passed.
4824
+ * @param onlyDirectRoutes - Restricts a fetched quote to single-hop routes.
4825
+ * @param maxAccounts - Account budget for a fetched route.
4826
+ * @param reduceOnly - Which side must not increase in magnitude; enforced by `endSwap`.
4827
+ * @param quote - Pre-fetched quote. Authoritative when passed: its `swapMode` is the effective
4828
+ * mode, and it must be for this pair and this `amount`.
4880
4829
  * @param userAccountPublicKey - Optional user account override (e.g. when the account is being
4881
- * created in the same transaction).
4882
- * @throws If no quote is passed and Jupiter's quote API returns none.
4883
- * @returns `ixs` instruction list (ATA creation, `beginSwap`, Jupiter swap instructions,
4884
- * `endSwap`, in order) and `lookupTables` needed to fit it in a versioned transaction.
4830
+ * created in the same transaction and not yet resolvable via `getUserAccountPublicKey`).
4831
+ * @throws If the quote passed in or freshly fetched is for a different pair or a different
4832
+ * size than the swap being built.
4833
+ * @returns `ixs` ATA creation, `beginSwap`, the route's instructions, `endSwap`, in order
4834
+ * and the `lookupTables` needed to fit them in a versioned transaction.
4885
4835
  */
4886
- async getJupiterSwapIxV6({ jupiterClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, onlyDirectRoutes, quote, reduceOnly, userAccountPublicKey, }) {
4836
+ async getProviderSwapIx({ swapProvider, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, onlyDirectRoutes, maxAccounts, reduceOnly, quote, userAccountPublicKey, }) {
4837
+ var _a, _b;
4887
4838
  const outMarket = this.getSpotMarketAccountOrThrow(outMarketIndex);
4888
4839
  const inMarket = this.getSpotMarketAccountOrThrow(inMarketIndex);
4889
- if (!quote) {
4890
- const fetchedQuote = await jupiterClient.getQuote({
4891
- inputMint: inMarket.mint,
4892
- outputMint: outMarket.mint,
4893
- amount,
4894
- slippageBps,
4895
- swapMode,
4896
- onlyDirectRoutes,
4897
- });
4898
- quote = fetchedQuote;
4899
- }
4900
- if (!quote) {
4901
- throw new Error('Could not fetch swap quote. Please try again.');
4902
- }
4903
- const isExactOut = swapMode === 'ExactOut' || quote.swapMode === 'ExactOut';
4904
- const amountIn = new anchor_1.BN(quote.inAmount);
4905
- const exactOutBufferedAmountIn = amountIn.muln(1001).divn(1000); // Add 10bp buffer
4906
- const transaction = await jupiterClient.getSwap({
4907
- quote,
4908
- userPublicKey: this.provider.wallet.publicKey,
4909
- slippageBps,
4910
- });
4911
- const { transactionMessage, lookupTables } = await jupiterClient.getTransactionMessageAndLookupTables({
4912
- transaction,
4913
- });
4914
- const jupiterInstructions = jupiterClient.getJupiterInstructions({
4915
- transactionMessage,
4840
+ const quoteToUse = quote !== null && quote !== void 0 ? quote : (await swapProvider.getQuote({
4916
4841
  inputMint: inMarket.mint,
4917
4842
  outputMint: outMarket.mint,
4918
- });
4843
+ amount,
4844
+ userPublicKey: this.provider.wallet.publicKey,
4845
+ slippageBps,
4846
+ swapMode: swapMode !== null && swapMode !== void 0 ? swapMode : 'ExactIn',
4847
+ onlyDirectRoutes,
4848
+ maxAccounts,
4849
+ sizeConstraint: UnifiedSwapClient_1.DEFAULT_ROUTE_SIZE_CONSTRAINT,
4850
+ }));
4851
+ // The quote's own mode decides which side `amount` names and how `beginSwap` is sized, so it
4852
+ // wins over `swapMode` whether the quote was passed in or just fetched — a provider that
4853
+ // answers in the other mode is then caught by the size check rather than sizing the wrong side.
4854
+ const effectiveSwapMode = (_b = (_a = quoteToUse.swapMode) !== null && _a !== void 0 ? _a : swapMode) !== null && _b !== void 0 ? _b : 'ExactIn';
4855
+ // Both paths, identically: a freshly fetched quote is no more trustworthy about what it
4856
+ // priced than one handed to us.
4857
+ this.assertQuoteMatchesMarkets(quoteToUse, inMarket, outMarket);
4858
+ this.assertQuoteMatchesAmount(quoteToUse, amount, effectiveSwapMode);
4859
+ // Size `beginSwap` off the quote's own input: under ExactOut `amount` is the requested
4860
+ // output, so buffering it would be a guess at what the route consumes.
4861
+ const quotedAmountIn = new anchor_1.BN(quoteToUse.inAmount);
4862
+ const amountIn = effectiveSwapMode === 'ExactOut'
4863
+ ? quotedAmountIn.muln(1001).divn(1000) // Add 10bp buffer
4864
+ : quotedAmountIn;
4919
4865
  const preInstructions = [];
4920
4866
  if (!outAssociatedTokenAccount) {
4921
- const tokenProgram = this.getTokenProgramForSpotMarket(outMarket);
4922
- outAssociatedTokenAccount = await this.getAssociatedTokenAccount(outMarket.marketIndex, false, tokenProgram);
4923
- const accountInfo = await this.connection.getAccountInfo(outAssociatedTokenAccount);
4924
- if (!accountInfo) {
4925
- preInstructions.push(this.createAssociatedTokenAccountIdempotentInstruction(outAssociatedTokenAccount, this.provider.wallet.publicKey, this.provider.wallet.publicKey, outMarket.mint, tokenProgram));
4867
+ const { tokenAccount, createIx } = await this.getOrCreateSwapTokenAccount(outMarket);
4868
+ outAssociatedTokenAccount = tokenAccount;
4869
+ if (createIx) {
4870
+ preInstructions.push(createIx);
4926
4871
  }
4927
4872
  }
4928
4873
  if (!inAssociatedTokenAccount) {
4929
- const tokenProgram = this.getTokenProgramForSpotMarket(inMarket);
4930
- inAssociatedTokenAccount = await this.getAssociatedTokenAccount(inMarket.marketIndex, false, tokenProgram);
4931
- const accountInfo = await this.connection.getAccountInfo(inAssociatedTokenAccount);
4932
- if (!accountInfo) {
4933
- preInstructions.push(this.createAssociatedTokenAccountIdempotentInstruction(inAssociatedTokenAccount, this.provider.wallet.publicKey, this.provider.wallet.publicKey, inMarket.mint, tokenProgram));
4874
+ const { tokenAccount, createIx } = await this.getOrCreateSwapTokenAccount(inMarket);
4875
+ inAssociatedTokenAccount = tokenAccount;
4876
+ if (createIx) {
4877
+ preInstructions.push(createIx);
4934
4878
  }
4935
4879
  }
4936
4880
  const { beginSwapIx, endSwapIx } = await this.getSwapIx({
4937
4881
  outMarketIndex,
4938
4882
  inMarketIndex,
4939
- amountIn: isExactOut ? exactOutBufferedAmountIn : amountIn,
4883
+ amountIn,
4940
4884
  inTokenAccount: inAssociatedTokenAccount,
4941
4885
  outTokenAccount: outAssociatedTokenAccount,
4942
4886
  reduceOnly,
4943
4887
  userAccountPublicKey,
4944
4888
  });
4945
- const ixs = [
4946
- ...preInstructions,
4947
- beginSwapIx,
4948
- ...jupiterInstructions,
4949
- endSwapIx,
4950
- ];
4951
- return { ixs, lookupTables };
4889
+ const { instructions: routeInstructions, lookupTables } = await swapProvider.getRouteInstructions({
4890
+ quote: quoteToUse,
4891
+ userPublicKey: this.provider.wallet.publicKey,
4892
+ });
4893
+ return {
4894
+ ixs: [...preInstructions, beginSwapIx, ...routeInstructions, endSwapIx],
4895
+ lookupTables,
4896
+ };
4952
4897
  }
4953
4898
  /**
4954
4899
  * Builds the `beginSwap`/`endSwap` instruction pair that must bracket an external swap
@@ -5053,88 +4998,6 @@ class VelocityClient {
5053
4998
  });
5054
4999
  return { beginSwapIx, endSwapIx };
5055
5000
  }
5056
- /**
5057
- * Builds the instruction list for a swap routed through a `UnifiedSwapClient` (the current
5058
- * preferred swap path). Creates any missing associated token accounts and wraps the client's
5059
- * routing instructions between `beginSwap`/`endSwap`. See `swap` for parameter semantics;
5060
- * `amount` is in the "in" token's mint decimals (or "out" token's decimals when `swapMode` is
5061
- * `ExactOut`).
5062
- * @param userAccountPublicKey - Optional user account override (e.g. when the account is being
5063
- * created in the same transaction).
5064
- * @returns `ixs` — instruction list (ATA creation, `beginSwap`, routed swap instructions,
5065
- * `endSwap`, in order) and `lookupTables` needed to fit it in a versioned transaction.
5066
- */
5067
- async getSwapIxV2({ swapClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, onlyDirectRoutes, reduceOnly, quote, v6, userAccountPublicKey, }) {
5068
- // Get market accounts to determine mints
5069
- const outMarket = this.getSpotMarketAccountOrThrow(outMarketIndex);
5070
- const inMarket = this.getSpotMarketAccountOrThrow(inMarketIndex);
5071
- const isExactOut = swapMode === 'ExactOut';
5072
- const preInstructions = [];
5073
- // Handle token accounts if not provided
5074
- let finalOutAssociatedTokenAccount = outAssociatedTokenAccount;
5075
- let finalInAssociatedTokenAccount = inAssociatedTokenAccount;
5076
- if (!finalOutAssociatedTokenAccount) {
5077
- const tokenProgram = this.getTokenProgramForSpotMarket(outMarket);
5078
- finalOutAssociatedTokenAccount = await this.getAssociatedTokenAccount(outMarket.marketIndex, false, tokenProgram);
5079
- const accountInfo = await this.connection.getAccountInfo(finalOutAssociatedTokenAccount);
5080
- if (!accountInfo) {
5081
- preInstructions.push(this.createAssociatedTokenAccountIdempotentInstruction(finalOutAssociatedTokenAccount, this.provider.wallet.publicKey, this.provider.wallet.publicKey, outMarket.mint, tokenProgram));
5082
- }
5083
- }
5084
- if (!finalInAssociatedTokenAccount) {
5085
- const tokenProgram = this.getTokenProgramForSpotMarket(inMarket);
5086
- finalInAssociatedTokenAccount = await this.getAssociatedTokenAccount(inMarket.marketIndex, false, tokenProgram);
5087
- const accountInfo = await this.connection.getAccountInfo(finalInAssociatedTokenAccount);
5088
- if (!accountInfo) {
5089
- preInstructions.push(this.createAssociatedTokenAccountIdempotentInstruction(finalInAssociatedTokenAccount, this.provider.wallet.publicKey, this.provider.wallet.publicKey, inMarket.mint, tokenProgram));
5090
- }
5091
- }
5092
- let amountInForBeginSwap;
5093
- if (isExactOut) {
5094
- if (quote || (v6 === null || v6 === void 0 ? void 0 : v6.quote)) {
5095
- amountInForBeginSwap = (v6 === null || v6 === void 0 ? void 0 : v6.quote)
5096
- ? new anchor_1.BN(v6.quote.inAmount)
5097
- : new anchor_1.BN(quote.inAmount);
5098
- }
5099
- else {
5100
- amountInForBeginSwap = amount.muln(1001).divn(1000);
5101
- }
5102
- }
5103
- else {
5104
- amountInForBeginSwap = amount;
5105
- }
5106
- // Get velocity swap instructions for begin and end
5107
- const { beginSwapIx, endSwapIx } = await this.getSwapIx({
5108
- outMarketIndex,
5109
- inMarketIndex,
5110
- amountIn: amountInForBeginSwap,
5111
- inTokenAccount: finalInAssociatedTokenAccount,
5112
- outTokenAccount: finalOutAssociatedTokenAccount,
5113
- reduceOnly,
5114
- userAccountPublicKey,
5115
- });
5116
- // Get core swap instructions from SwapClient
5117
- const swapResult = await swapClient.getSwapInstructions({
5118
- inputMint: inMarket.mint,
5119
- outputMint: outMarket.mint,
5120
- amount,
5121
- userPublicKey: this.provider.wallet.publicKey,
5122
- slippageBps,
5123
- swapMode,
5124
- onlyDirectRoutes,
5125
- quote: quote !== null && quote !== void 0 ? quote : v6 === null || v6 === void 0 ? void 0 : v6.quote,
5126
- });
5127
- const allInstructions = [
5128
- ...preInstructions,
5129
- beginSwapIx,
5130
- ...swapResult.instructions,
5131
- endSwapIx,
5132
- ];
5133
- return {
5134
- ixs: allInstructions,
5135
- lookupTables: swapResult.lookupTables,
5136
- };
5137
- }
5138
5001
  /**
5139
5002
  * Converts a portion of the user's deposited wSOL (spot market index 1) into mSOL (spot market
5140
5003
  * index 2) by staking it with Marinade Finance, then swapping the resulting mSOL back into the
@@ -5278,11 +5141,12 @@ class VelocityClient {
5278
5141
  }
5279
5142
  /**
5280
5143
  * Keeper instruction: trips the authority-wide equity floor breaker. Proves on-chain that the
5281
- * given subaccount's cross-margin total collateral is below its `equityFloor` (reverts with
5282
- * `SufficientCollateral` otherwise, or if no floor is set) and sets `equityBreakerTripped` on the
5283
- * authority's `UserStats` every subaccount of the authority then rejects risk-increasing fills,
5284
- * withdrawals and transfers out until the warm admin calls `resetEquityFloorBreaker`.
5285
- * Permissionless any signer may trip it; the margin calculation is the proof.
5144
+ * given subaccount's net equity (unweighted assets and perp PnL minus spot liabilities) is below
5145
+ * its `equityFloor` (reverts with `SufficientCollateral` otherwise, if no floor is set, or with
5146
+ * `InvalidOracle` if any of the subaccount's oracles is invalid) and sets `equityBreakerTripped`
5147
+ * on the authority's `UserStats` every subaccount of the authority then rejects risk-increasing
5148
+ * fills, withdrawals and transfers out until the warm admin calls `resetEquityFloorBreaker`.
5149
+ * Permissionless — any signer may trip it; the equity calculation is the proof.
5286
5150
  * @param userAccountPublicKey - Public key of the breached subaccount's user account.
5287
5151
  * @param user - Decoded user account of the breached subaccount.
5288
5152
  * @param txParams - Optional compute-unit/priority-fee overrides.
@@ -6802,11 +6666,13 @@ class VelocityClient {
6802
6666
  * @param userAccountPublicKey - Public key of the user account being liquidated.
6803
6667
  * @param liquidatorSubAccountId - Liquidator's sub-account to credit; defaults to the active sub-account.
6804
6668
  * @param maxAccounts - Caps the number of accounts Jupiter's route may use.
6805
- * @throws If no quote can be fetched and `quote` was not supplied.
6669
+ * @throws If no quote can be fetched and `quote` was not supplied, or if the quote — passed in or
6670
+ * freshly fetched — is for a different pair or a different size than the swap being built.
6806
6671
  * @returns The ordered instructions (pre-instructions, `beginSwap`, Jupiter swap, `endSwap`) and
6807
6672
  * any address lookup tables the Jupiter route requires.
6808
6673
  */
6809
6674
  async getJupiterLiquidateSpotWithSwapIxV6({ jupiterClient, liabilityMarketIndex, assetMarketIndex, swapAmount, assetTokenAccount, liabilityTokenAccount, slippageBps, swapMode, onlyDirectRoutes, quote, userAccount, userAccountPublicKey, liquidatorSubAccountId, maxAccounts, }) {
6675
+ var _a, _b;
6810
6676
  const liabilityMarket = this.getSpotMarketAccountOrThrow(liabilityMarketIndex);
6811
6677
  const assetMarket = this.getSpotMarketAccountOrThrow(assetMarketIndex);
6812
6678
  if (!quote) {
@@ -6824,19 +6690,12 @@ class VelocityClient {
6824
6690
  if (!quote) {
6825
6691
  throw new Error('Could not fetch swap quote. Please try again.');
6826
6692
  }
6693
+ this.assertQuoteMatchesMarkets(quote, assetMarket, liabilityMarket);
6694
+ this.assertQuoteMatchesAmount(quote, swapAmount, (_b = (_a = quote.swapMode) !== null && _a !== void 0 ? _a : swapMode) !== null && _b !== void 0 ? _b : 'ExactIn');
6827
6695
  const amountIn = new anchor_1.BN(quote.inAmount);
6828
- const transaction = await jupiterClient.getSwap({
6696
+ const { instructions: jupiterInstructions, lookupTables } = await jupiterClient.getRouteInstructions({
6829
6697
  quote,
6830
6698
  userPublicKey: this.provider.wallet.publicKey,
6831
- slippageBps,
6832
- });
6833
- const { transactionMessage, lookupTables } = await jupiterClient.getTransactionMessageAndLookupTables({
6834
- transaction,
6835
- });
6836
- const jupiterInstructions = jupiterClient.getJupiterInstructions({
6837
- transactionMessage,
6838
- inputMint: assetMarket.mint,
6839
- outputMint: liabilityMarket.mint,
6840
6699
  });
6841
6700
  const preInstructions = [];
6842
6701
  if (!liabilityTokenAccount) {
@@ -17,7 +17,7 @@ import { AddressLookupTableAccount, Keypair, PublicKey, TransactionInstruction,
17
17
  import { FeeStructure, OracleGuardRails, OracleSource, ExchangeStatus, SolvencyStatus, MarketStatus, ContractTier, AssetTier, TxParams, AddAmmConstituentMappingDatum, SwapReduceOnly, InitializeConstituentParams, ConstituentStatus, LPPoolAccount, TransferFeeAndPnlPoolDirection, MarketType } from './types';
18
18
  import { BN } from './isomorphic/anchor';
19
19
  import { VelocityClient } from './velocityClient';
20
- import { JupiterClient, QuoteResponse } from './jupiter/jupiterClient';
20
+ import { JupiterClient, JupiterSwapQuote } from './jupiter/jupiterClient';
21
21
  import { SwapMode } from './swap/UnifiedSwapClient';
22
22
  export declare class AdminClient extends VelocityClient {
23
23
  /**
@@ -2283,7 +2283,7 @@ export declare class AdminClient extends VelocityClient {
2283
2283
  slippageBps?: number;
2284
2284
  swapMode?: SwapMode;
2285
2285
  onlyDirectRoutes?: boolean;
2286
- quote?: QuoteResponse;
2286
+ quote?: JupiterSwapQuote;
2287
2287
  lpPoolId: number;
2288
2288
  }): Promise<{
2289
2289
  ixs: TransactionInstruction[];
@@ -2500,24 +2500,27 @@ export declare class AdminClient extends VelocityClient {
2500
2500
  */
2501
2501
  getUpdateSpecialUserStatusIx(userAccountPublicKey: PublicKey, status: number): Promise<TransactionInstruction>;
2502
2502
  /**
2503
- * Sets a `User` account's `equityFloor`: the minimum cross-margin total
2504
- * collateral (QUOTE_PRECISION) the account must keep. Below the floor the
2505
- * program rejects risk-increasing order placement and fills, withdrawals,
2506
- * and deposit/position transfers out of the account; reduce-only activity
2507
- * stays allowed. Requires warm admin (`check_warm`); the account's
2508
- * authority and delegate cannot change it. Pass `0` to disable.
2503
+ * Sets a `User` account's `equityFloor` and `equityFloorBuffer`. The floor
2504
+ * is the minimum cross-margin total collateral (QUOTE_PRECISION) below
2505
+ * which the permissionless breaker can trip; risk-increasing order
2506
+ * placement and fills, withdrawals, and deposit/position transfers out of
2507
+ * the account must clear `floor + buffer` (`EquityBelowFloor` otherwise);
2508
+ * reduce-only activity stays allowed. Requires warm admin (`check_warm`);
2509
+ * the account's authority and delegate cannot change either value. Pass
2510
+ * `0` as the floor to disable both checks.
2509
2511
  * @param userAccountPublicKey - `User` PDA to update.
2510
2512
  * @param equityFloor - New floor, QUOTE_PRECISION; `0` disables.
2513
+ * @param equityFloorBuffer - New buffer, QUOTE_PRECISION; no effect while the floor is 0.
2511
2514
  * @param txParams - Optional transaction-building overrides.
2512
2515
  * @returns Transaction signature.
2513
2516
  */
2514
- updateUserEquityFloor(userAccountPublicKey: PublicKey, equityFloor: BN, txParams?: TxParams): Promise<TransactionSignature>;
2517
+ updateUserEquityFloor(userAccountPublicKey: PublicKey, equityFloor: BN, equityFloorBuffer: BN, txParams?: TxParams): Promise<TransactionSignature>;
2515
2518
  /**
2516
2519
  * Builds the `updateUserEquityFloor` instruction without sending it. See
2517
2520
  * `updateUserEquityFloor`.
2518
2521
  * @returns The unsigned `updateUserEquityFloor` instruction.
2519
2522
  */
2520
- getUpdateUserEquityFloorIx(userAccountPublicKey: PublicKey, equityFloor: BN): Promise<TransactionInstruction>;
2523
+ getUpdateUserEquityFloorIx(userAccountPublicKey: PublicKey, equityFloor: BN, equityFloorBuffer: BN): Promise<TransactionInstruction>;
2521
2524
  /**
2522
2525
  * Clears the authority-wide equity floor breaker set by the permissionless
2523
2526
  * `tripEquityFloorBreaker` keeper instruction, unfreezing all of the