@0dotxyz/p0-ts-sdk 2.2.0-alpha.4 → 2.2.0-alpha.6

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.
package/dist/index.cjs CHANGED
@@ -11,6 +11,8 @@ var bufferLayoutUtils = require('@solana/buffer-layout-utils');
11
11
  var buffer = require('buffer');
12
12
  var borsh$1 = require('borsh');
13
13
  var borsh = require('@coral-xyz/borsh');
14
+ var WebSocket = require('ws');
15
+ var msgpack = require('@msgpack/msgpack');
14
16
  var api = require('@jup-ag/api');
15
17
  var onDemand = require('@switchboard-xyz/on-demand');
16
18
  var common = require('@switchboard-xyz/common');
@@ -39,6 +41,7 @@ var BigNumber3__default = /*#__PURE__*/_interopDefault(BigNumber3);
39
41
  var BN11__default = /*#__PURE__*/_interopDefault(BN11);
40
42
  var Decimal3__default = /*#__PURE__*/_interopDefault(Decimal3);
41
43
  var borsh__namespace = /*#__PURE__*/_interopNamespace(borsh);
44
+ var WebSocket__default = /*#__PURE__*/_interopDefault(WebSocket);
42
45
 
43
46
  // src/config.ts
44
47
 
@@ -115,12 +118,15 @@ function getConfig(environment = "production", overrides) {
115
118
 
116
119
  // src/errors/transaction-building.errors.ts
117
120
  var TransactionBuildingErrorCode = /* @__PURE__ */ ((TransactionBuildingErrorCode2) => {
118
- TransactionBuildingErrorCode2["JUPITER_SWAP_SIZE_EXCEEDED_REPAY"] = "JUPITER_SWAP_SIZE_EXCEEDED_REPAY";
119
- TransactionBuildingErrorCode2["JUPITER_SWAP_SIZE_EXCEEDED_LOOP"] = "JUPITER_SWAP_SIZE_EXCEEDED_LOOP";
121
+ TransactionBuildingErrorCode2["SWAP_SIZE_EXCEEDED_LOOP"] = "SWAP_SIZE_EXCEEDED_LOOP";
122
+ TransactionBuildingErrorCode2["SWAP_SIZE_EXCEEDED_REPAY"] = "SWAP_SIZE_EXCEEDED_REPAY";
123
+ TransactionBuildingErrorCode2["SWAP_SIZE_EXCEEDED_POSITION_SWAP"] = "SWAP_SIZE_EXCEEDED_POSITION_SWAP";
120
124
  TransactionBuildingErrorCode2["ORACLE_CRANK_FAILED"] = "ORACLE_CRANK_FAILED";
121
125
  TransactionBuildingErrorCode2["KAMINO_RESERVE_NOT_FOUND"] = "KAMINO_RESERVE_NOT_FOUND";
122
126
  TransactionBuildingErrorCode2["DRIFT_STATE_NOT_FOUND"] = "DRIFT_STATE_NOT_FOUND";
123
127
  TransactionBuildingErrorCode2["JUPLEND_STATE_NOT_FOUND"] = "JUPLEND_STATE_NOT_FOUND";
128
+ TransactionBuildingErrorCode2["SWITCHBOARD_FEED_UPDATE_FAILED"] = "SWITCHBOARD_FEED_UPDATE_FAILED";
129
+ TransactionBuildingErrorCode2["SWAP_QUOTE_FAILED"] = "SWAP_QUOTE_FAILED";
124
130
  return TransactionBuildingErrorCode2;
125
131
  })(TransactionBuildingErrorCode || {});
126
132
  var TransactionBuildingError = class _TransactionBuildingError extends Error {
@@ -135,21 +141,25 @@ var TransactionBuildingError = class _TransactionBuildingError extends Error {
135
141
  Error.captureStackTrace(this, _TransactionBuildingError);
136
142
  }
137
143
  }
138
- /**
139
- * Jupiter swap instruction size exceeds available transaction size
140
- */
141
- static jupiterSwapSizeExceededLoop(bytes, accountKeys) {
144
+ static swapSizeExceededLoop(bytes, accountKeys, provider) {
145
+ return new _TransactionBuildingError(
146
+ "SWAP_SIZE_EXCEEDED_LOOP" /* SWAP_SIZE_EXCEEDED_LOOP */,
147
+ `${provider ?? "Swap"} instruction size exceeds available transaction size`,
148
+ { bytes, accountKeys, provider }
149
+ );
150
+ }
151
+ static swapSizeExceededRepay(bytes, accountKeys, provider) {
142
152
  return new _TransactionBuildingError(
143
- "JUPITER_SWAP_SIZE_EXCEEDED_LOOP" /* JUPITER_SWAP_SIZE_EXCEEDED_LOOP */,
144
- "Jupiter swap instruction size exceeds available transaction size",
145
- { bytes, accountKeys }
153
+ "SWAP_SIZE_EXCEEDED_REPAY" /* SWAP_SIZE_EXCEEDED_REPAY */,
154
+ `${provider ?? "Swap"} instruction size exceeds available transaction size`,
155
+ { bytes, accountKeys, provider }
146
156
  );
147
157
  }
148
- static jupiterSwapSizeExceededRepay(bytes, accountKeys) {
158
+ static swapSizeExceededPositionSwap(bytes, accountKeys, provider) {
149
159
  return new _TransactionBuildingError(
150
- "JUPITER_SWAP_SIZE_EXCEEDED_REPAY" /* JUPITER_SWAP_SIZE_EXCEEDED_REPAY */,
151
- "Jupiter swap instruction size exceeds available transaction size",
152
- { bytes, accountKeys }
160
+ "SWAP_SIZE_EXCEEDED_POSITION_SWAP" /* SWAP_SIZE_EXCEEDED_POSITION_SWAP */,
161
+ `${provider ?? "Swap"} instruction size exceeds available transaction size`,
162
+ { bytes, accountKeys, provider }
153
163
  );
154
164
  }
155
165
  /**
@@ -193,6 +203,26 @@ var TransactionBuildingError = class _TransactionBuildingError extends Error {
193
203
  { bankAddress, bankMint, bankSymbol }
194
204
  );
195
205
  }
206
+ /**
207
+ * Failed to update Switchboard price feeds
208
+ */
209
+ static switchboardFeedUpdateFailed(oracleKeys, reason) {
210
+ return new _TransactionBuildingError(
211
+ "SWITCHBOARD_FEED_UPDATE_FAILED" /* SWITCHBOARD_FEED_UPDATE_FAILED */,
212
+ `Switchboard feed update failed: ${reason}`,
213
+ { oracleKeys, reason }
214
+ );
215
+ }
216
+ /**
217
+ * Failed to get a swap quote from any provider
218
+ */
219
+ static swapQuoteFailed(provider, inputMint, outputMint, reason) {
220
+ return new _TransactionBuildingError(
221
+ "SWAP_QUOTE_FAILED" /* SWAP_QUOTE_FAILED */,
222
+ `${provider} swap quote failed for ${inputMint} \u2192 ${outputMint}: ${reason}`,
223
+ { provider, inputMint, outputMint, reason }
224
+ );
225
+ }
196
226
  /**
197
227
  * Generic escape hatch for custom errors
198
228
  */
@@ -345,6 +375,7 @@ var MAX_U64 = BigInt("18446744073709551615").toString();
345
375
 
346
376
  // src/constants/transaction.consts.ts
347
377
  var MAX_TX_SIZE = 1232;
378
+ var MAX_ACCOUNT_LOCKS = 64;
348
379
  var BUNDLE_TX_SIZE = 81;
349
380
  var PRIORITY_TX_SIZE = 44;
350
381
  var WSOL_MINT = new web3_js.PublicKey("So11111111111111111111111111111111111111112");
@@ -14893,8 +14924,40 @@ function getTxSize(tx) {
14893
14924
  const signaturesSize = (numRequiredSignatures - numSigners) * 64 + 1;
14894
14925
  try {
14895
14926
  const baseTxSize = isVersioned ? tx.serialize().length : tx.serialize({ requireAllSignatures: false, verifySignatures: false }).length;
14896
- return baseTxSize + feePayerSize + signaturesSize;
14897
- } catch {
14927
+ const totalSize = baseTxSize + feePayerSize + signaturesSize;
14928
+ if (isVersioned && totalSize > 1232) {
14929
+ const { header, staticAccountKeys, addressTableLookups } = tx.message;
14930
+ const lutWritable = addressTableLookups.reduce((s, l) => s + l.writableIndexes.length, 0);
14931
+ const lutReadonly = addressTableLookups.reduce((s, l) => s + l.readonlyIndexes.length, 0);
14932
+ console.warn("[getTxSize] oversized TX", {
14933
+ totalSize,
14934
+ overshoot: totalSize - 1232,
14935
+ staticKeys: staticAccountKeys.length,
14936
+ numSignatures: header.numRequiredSignatures,
14937
+ numLuts: addressTableLookups.length,
14938
+ lutWritable,
14939
+ lutReadonly,
14940
+ totalAccounts: staticAccountKeys.length + lutWritable + lutReadonly
14941
+ });
14942
+ }
14943
+ return totalSize;
14944
+ } catch (err) {
14945
+ if (isVersioned) {
14946
+ const { header, staticAccountKeys, addressTableLookups } = tx.message;
14947
+ const lutWritable = addressTableLookups.reduce((s, l) => s + l.writableIndexes.length, 0);
14948
+ const lutReadonly = addressTableLookups.reduce((s, l) => s + l.readonlyIndexes.length, 0);
14949
+ console.warn("[getTxSize] serialize failed", {
14950
+ error: err.message,
14951
+ staticKeys: staticAccountKeys.length,
14952
+ numSignatures: header.numRequiredSignatures,
14953
+ numLuts: addressTableLookups.length,
14954
+ lutWritable,
14955
+ lutReadonly,
14956
+ totalAccounts: staticAccountKeys.length + lutWritable + lutReadonly
14957
+ });
14958
+ } else {
14959
+ console.warn("[getTxSize] serialize failed", { error: err.message });
14960
+ }
14898
14961
  return 9999;
14899
14962
  }
14900
14963
  }
@@ -14909,6 +14972,47 @@ function getAccountKeys(tx, lookupTableAccounts) {
14909
14972
  } else {
14910
14973
  return tx.compileMessage().getAccountKeys().length;
14911
14974
  }
14975
+ } catch (err) {
14976
+ console.warn("[getAccountKeys] decompile failed", { error: err.message });
14977
+ return 9999;
14978
+ }
14979
+ }
14980
+ function getWritableAccountKeys(tx) {
14981
+ const isVersioned = isV0Tx(tx);
14982
+ try {
14983
+ if (isVersioned) {
14984
+ const { header, staticAccountKeys, addressTableLookups } = tx.message;
14985
+ const writableStatic = staticAccountKeys.length - header.numReadonlySignedAccounts - header.numReadonlyUnsignedAccounts;
14986
+ const writableLut = addressTableLookups.reduce(
14987
+ (sum, lookup) => sum + lookup.writableIndexes.length,
14988
+ 0
14989
+ );
14990
+ return writableStatic + writableLut;
14991
+ } else {
14992
+ const message = tx.compileMessage();
14993
+ const { numRequiredSignatures, numReadonlySignedAccounts, numReadonlyUnsignedAccounts } = message.header;
14994
+ const totalKeys = message.accountKeys.length;
14995
+ const writableSigned = numRequiredSignatures - numReadonlySignedAccounts;
14996
+ const writableUnsigned = totalKeys - numRequiredSignatures - numReadonlyUnsignedAccounts;
14997
+ return writableSigned + writableUnsigned;
14998
+ }
14999
+ } catch {
15000
+ return 9999;
15001
+ }
15002
+ }
15003
+ function getTotalAccountKeys(tx) {
15004
+ const isVersioned = isV0Tx(tx);
15005
+ try {
15006
+ if (isVersioned) {
15007
+ const { staticAccountKeys, addressTableLookups } = tx.message;
15008
+ const lutAccounts = addressTableLookups.reduce(
15009
+ (sum, lookup) => sum + lookup.writableIndexes.length + lookup.readonlyIndexes.length,
15010
+ 0
15011
+ );
15012
+ return staticAccountKeys.length + lutAccounts;
15013
+ } else {
15014
+ return tx.compileMessage().accountKeys.length;
15015
+ }
14912
15016
  } catch {
14913
15017
  return 9999;
14914
15018
  }
@@ -20146,6 +20250,14 @@ var HealthCacheSimulationError = class _HealthCacheSimulationError extends Error
20146
20250
  }
20147
20251
  };
20148
20252
 
20253
+ // src/services/account/types/action.types.ts
20254
+ var SwapProvider = /* @__PURE__ */ ((SwapProvider2) => {
20255
+ SwapProvider2["JUPITER"] = "JUPITER";
20256
+ SwapProvider2["TITAN"] = "TITAN";
20257
+ SwapProvider2["DFLOW"] = "DFLOW";
20258
+ return SwapProvider2;
20259
+ })(SwapProvider || {});
20260
+
20149
20261
  // src/services/account/utils/deserialize.utils.ts
20150
20262
  var EMPTY_HEALTH_CACHE = {
20151
20263
  assetValue: {
@@ -44194,6 +44306,346 @@ function makeUpdateJupLendRate({ lendingState }) {
44194
44306
  lendingState.rewardsRateModel
44195
44307
  );
44196
44308
  }
44309
+ var SUBPROTOCOL = "v1.api.titan.ag";
44310
+ var UINT64_MAX = (1n << 64n) - 1n;
44311
+ function toBigInt(value) {
44312
+ if (typeof value === "bigint") {
44313
+ if (value < 0n || value > UINT64_MAX) {
44314
+ throw new RangeError(`Amount out of uint64 range: ${value}`);
44315
+ }
44316
+ return value;
44317
+ }
44318
+ if (!Number.isInteger(value)) {
44319
+ throw new TypeError(`Amount must be a whole number, got ${value}`);
44320
+ }
44321
+ if (value < 0) {
44322
+ throw new RangeError(`Amount must be non-negative, got ${value}`);
44323
+ }
44324
+ return BigInt(value);
44325
+ }
44326
+ var ConnectionClosed = class _ConnectionClosed extends Error {
44327
+ code;
44328
+ reason;
44329
+ constructor(code, reason) {
44330
+ super(`Client WebSocket closed with code ${code}: ${reason}`);
44331
+ this.name = "ConnectionClosed";
44332
+ Object.setPrototypeOf(this, _ConnectionClosed.prototype);
44333
+ this.code = code;
44334
+ this.reason = reason;
44335
+ }
44336
+ };
44337
+ var ErrorResponse = class _ErrorResponse extends Error {
44338
+ response;
44339
+ constructor(response) {
44340
+ super(`Request ${response.requestId} failed with code ${response.code}: ${response.message}`);
44341
+ this.name = "ErrorResponse";
44342
+ Object.setPrototypeOf(this, _ErrorResponse.prototype);
44343
+ this.response = response;
44344
+ }
44345
+ };
44346
+ var StreamError = class _StreamError extends Error {
44347
+ streamId;
44348
+ errorCode;
44349
+ errorMessage;
44350
+ constructor(packet) {
44351
+ const code = packet.errorCode ?? 0;
44352
+ const message = packet.errorMessage ?? "";
44353
+ super(`Stream ${packet.id} ended with error code ${code}: ${message}`);
44354
+ this.name = "StreamError";
44355
+ Object.setPrototypeOf(this, _StreamError.prototype);
44356
+ this.streamId = packet.id;
44357
+ this.errorCode = code;
44358
+ this.errorMessage = message;
44359
+ }
44360
+ };
44361
+ var encoder = new msgpack.Encoder({ useBigInt64: true });
44362
+ var decoder = new msgpack.Decoder({ useBigInt64: true });
44363
+ var V1Client = class _V1Client {
44364
+ socket;
44365
+ nextId = 0;
44366
+ _closed = false;
44367
+ _closing = false;
44368
+ pending = /* @__PURE__ */ new Map();
44369
+ streams = /* @__PURE__ */ new Map();
44370
+ streamStopping = /* @__PURE__ */ new Map();
44371
+ closeListeners = [];
44372
+ // --- Static connect ---
44373
+ static connect(url) {
44374
+ return new Promise((resolve, reject) => {
44375
+ const ws = new WebSocket__default.default(url, [SUBPROTOCOL]);
44376
+ ws.binaryType = "arraybuffer";
44377
+ const onOpen = () => {
44378
+ ws.off("error", onError);
44379
+ ws.off("close", onClose);
44380
+ resolve(new _V1Client(ws));
44381
+ };
44382
+ const onError = (err) => {
44383
+ ws.off("open", onOpen);
44384
+ ws.off("close", onClose);
44385
+ reject(err);
44386
+ };
44387
+ const onClose = (code, reason) => {
44388
+ ws.off("open", onOpen);
44389
+ ws.off("error", onError);
44390
+ reject(
44391
+ new Error(
44392
+ `WebSocket closed before open (code=${code}${reason.length ? `, reason=${reason.toString()}` : ""})`
44393
+ )
44394
+ );
44395
+ };
44396
+ ws.once("open", onOpen);
44397
+ ws.once("error", onError);
44398
+ ws.once("close", onClose);
44399
+ });
44400
+ }
44401
+ // --- Constructor ---
44402
+ constructor(socket) {
44403
+ this.socket = socket;
44404
+ this.socket.on("message", (data) => {
44405
+ this.handleMessage(data);
44406
+ });
44407
+ this.socket.on("close", (code, reason) => {
44408
+ this.handleClose(code, reason.toString());
44409
+ });
44410
+ this.socket.on("error", (err) => {
44411
+ this.handleError(err);
44412
+ });
44413
+ }
44414
+ nextRequestId() {
44415
+ return this.nextId++;
44416
+ }
44417
+ // --- Public API ---
44418
+ get closed() {
44419
+ return this._closed;
44420
+ }
44421
+ close() {
44422
+ if (this._closed) return Promise.resolve();
44423
+ return new Promise((resolve, reject) => {
44424
+ this.closeListeners.push({ resolve, reject });
44425
+ if (!this._closing) {
44426
+ this._closing = true;
44427
+ this.socket.close();
44428
+ }
44429
+ });
44430
+ }
44431
+ newSwapQuoteStream(params) {
44432
+ const requestId = this.nextRequestId();
44433
+ const promise = new Promise(
44434
+ (resolve, reject) => {
44435
+ this.pending.set(requestId, {
44436
+ resolve,
44437
+ reject,
44438
+ kind: "NewSwapQuoteStream"
44439
+ });
44440
+ }
44441
+ );
44442
+ const normalized = {
44443
+ ...params,
44444
+ swap: { ...params.swap, amount: toBigInt(params.swap.amount) }
44445
+ };
44446
+ const message = {
44447
+ id: requestId,
44448
+ data: { NewSwapQuoteStream: normalized }
44449
+ };
44450
+ this.send(message);
44451
+ return promise;
44452
+ }
44453
+ stopStream(streamId) {
44454
+ const requestId = this.nextRequestId();
44455
+ const promise = new Promise((resolve, reject) => {
44456
+ this.pending.set(requestId, {
44457
+ resolve,
44458
+ reject,
44459
+ kind: "StopStream"
44460
+ });
44461
+ });
44462
+ const message = {
44463
+ id: requestId,
44464
+ data: { StopStream: { id: streamId } }
44465
+ };
44466
+ this.send(message);
44467
+ return promise;
44468
+ }
44469
+ // --- Send ---
44470
+ send(message) {
44471
+ try {
44472
+ const encoded = encoder.encode(message);
44473
+ this.socket.send(encoded);
44474
+ } catch (err) {
44475
+ const req = this.pending.get(message.id);
44476
+ if (req) {
44477
+ this.pending.delete(message.id);
44478
+ req.reject(err);
44479
+ }
44480
+ }
44481
+ }
44482
+ // --- Message handling ---
44483
+ handleMessage(raw) {
44484
+ let buf;
44485
+ if (raw instanceof ArrayBuffer) {
44486
+ buf = new Uint8Array(raw);
44487
+ } else if (Buffer.isBuffer(raw)) {
44488
+ buf = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
44489
+ } else if (Array.isArray(raw)) {
44490
+ buf = new Uint8Array(Buffer.concat(raw));
44491
+ } else {
44492
+ return;
44493
+ }
44494
+ let message;
44495
+ try {
44496
+ message = decoder.decode(buf);
44497
+ } catch {
44498
+ this.socket.close(3002, "failed to decode message");
44499
+ return;
44500
+ }
44501
+ if ("Response" in message) {
44502
+ this.handleResponse(message.Response);
44503
+ } else if ("Error" in message) {
44504
+ this.handleResponseError(message.Error);
44505
+ } else if ("StreamData" in message) {
44506
+ this.handleStreamData(message.StreamData);
44507
+ } else if ("StreamEnd" in message) {
44508
+ this.handleStreamEnd(message.StreamEnd);
44509
+ }
44510
+ }
44511
+ handleResponse(msg) {
44512
+ const req = this.pending.get(msg.requestId);
44513
+ if (!req) return;
44514
+ this.pending.delete(msg.requestId);
44515
+ if ("NewSwapQuoteStream" in msg.data && req.kind === "NewSwapQuoteStream") {
44516
+ const streamInfo = msg.stream;
44517
+ if (!streamInfo) {
44518
+ req.reject(new Error("No stream associated with NewSwapQuoteStream response"));
44519
+ return;
44520
+ }
44521
+ const stream = new ReadableStream({
44522
+ start: (controller) => {
44523
+ this.streams.set(streamInfo.id, controller);
44524
+ },
44525
+ cancel: () => {
44526
+ return this.cancelStream(streamInfo.id);
44527
+ }
44528
+ });
44529
+ const result = {
44530
+ response: msg.data.NewSwapQuoteStream,
44531
+ stream,
44532
+ streamId: streamInfo.id
44533
+ };
44534
+ req.resolve(result);
44535
+ } else if ("StreamStopped" in msg.data && req.kind === "StopStream") {
44536
+ req.resolve(msg.data.StreamStopped);
44537
+ } else {
44538
+ req.reject(new Error(`Unexpected response type for ${req.kind}`));
44539
+ }
44540
+ }
44541
+ handleResponseError(error) {
44542
+ const req = this.pending.get(error.requestId);
44543
+ if (!req) return;
44544
+ this.pending.delete(error.requestId);
44545
+ req.reject(new ErrorResponse(error));
44546
+ }
44547
+ handleStreamData(packet) {
44548
+ const controller = this.streams.get(packet.id);
44549
+ if (!controller) return;
44550
+ if (packet.payload.SwapQuotes !== void 0) {
44551
+ controller.enqueue(packet.payload.SwapQuotes);
44552
+ }
44553
+ }
44554
+ handleStreamEnd(packet) {
44555
+ const controller = this.streams.get(packet.id);
44556
+ if (!controller) return;
44557
+ this.streams.delete(packet.id);
44558
+ this.streamStopping.delete(packet.id);
44559
+ if (packet.errorCode !== void 0) {
44560
+ controller.error(new StreamError(packet));
44561
+ } else {
44562
+ controller.close();
44563
+ }
44564
+ }
44565
+ async cancelStream(streamId) {
44566
+ if (this.streamStopping.get(streamId) || !this.streams.has(streamId)) return;
44567
+ this.streamStopping.set(streamId, true);
44568
+ await this.stopStream(streamId);
44569
+ }
44570
+ // --- Connection lifecycle ---
44571
+ rejectAll(error) {
44572
+ for (const req of this.pending.values()) {
44573
+ req.reject(error);
44574
+ }
44575
+ this.pending.clear();
44576
+ for (const controller of this.streams.values()) {
44577
+ controller.error(error);
44578
+ }
44579
+ this.streams.clear();
44580
+ this.streamStopping.clear();
44581
+ }
44582
+ handleClose(code, reason) {
44583
+ this._closed = true;
44584
+ this.rejectAll(new ConnectionClosed(code, reason));
44585
+ for (const listener of this.closeListeners) {
44586
+ listener.resolve();
44587
+ }
44588
+ this.closeListeners = [];
44589
+ }
44590
+ handleError(err) {
44591
+ this.rejectAll(err);
44592
+ this.socket.close(3002);
44593
+ }
44594
+ };
44595
+ function deserializeSerializedInstruction(ix) {
44596
+ return new web3_js.TransactionInstruction({
44597
+ programId: new web3_js.PublicKey(Buffer.from(ix.p, "base64")),
44598
+ keys: ix.a.map((account) => ({
44599
+ pubkey: new web3_js.PublicKey(Buffer.from(account.p, "base64")),
44600
+ isSigner: account.s,
44601
+ isWritable: account.w
44602
+ })),
44603
+ data: Buffer.from(ix.d, "base64")
44604
+ });
44605
+ }
44606
+ function selectBestRoute(quotes, swapMode) {
44607
+ const routes = Object.values(quotes);
44608
+ if (routes.length === 0) return null;
44609
+ return routes.reduce((best, route) => {
44610
+ if (swapMode === "ExactIn") {
44611
+ return route.outAmount > best.outAmount ? route : best;
44612
+ } else {
44613
+ return route.inAmount < best.inAmount ? route : best;
44614
+ }
44615
+ });
44616
+ }
44617
+ function buildSwapQuoteResult(route, swapMode) {
44618
+ const slippageBps = route.slippageBps;
44619
+ let otherAmountThreshold;
44620
+ if (swapMode === "ExactIn") {
44621
+ otherAmountThreshold = String(Math.floor(route.outAmount * (1 - slippageBps / 1e4)));
44622
+ } else {
44623
+ otherAmountThreshold = String(Math.ceil(route.inAmount * (1 + slippageBps / 1e4)));
44624
+ }
44625
+ return {
44626
+ inAmount: String(route.inAmount),
44627
+ outAmount: String(route.outAmount),
44628
+ otherAmountThreshold,
44629
+ slippageBps,
44630
+ platformFee: route.platformFee ? {
44631
+ amount: String(route.platformFee.amount),
44632
+ feeBps: route.platformFee.fee_bps
44633
+ } : void 0,
44634
+ contextSlot: route.contextSlot,
44635
+ timeTaken: route.timeTaken
44636
+ };
44637
+ }
44638
+ async function resolveLookupTables(connection, lutPubkeys) {
44639
+ if (lutPubkeys.length === 0) return [];
44640
+ const lutAccountsRaw = await connection.getMultipleAccountsInfo(lutPubkeys);
44641
+ return lutAccountsRaw.map((accountInfo, index) => {
44642
+ if (!accountInfo) return null;
44643
+ return new web3_js.AddressLookupTableAccount({
44644
+ key: lutPubkeys[index],
44645
+ state: web3_js.AddressLookupTableAccount.deserialize(accountInfo.data)
44646
+ });
44647
+ }).filter((account) => account !== null);
44648
+ }
44197
44649
 
44198
44650
  // src/vendor/klend/utils/klend/interest-rate.utils.ts
44199
44651
  function getKaminoTotalSupply(reserve) {
@@ -46715,18 +47167,18 @@ async function buildLoopFlashloanTx({
46715
47167
  overrideInferAccounts,
46716
47168
  blockhash
46717
47169
  }) {
46718
- const swapResult = [];
46719
47170
  const cuRequestIxs = [
46720
47171
  web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
46721
47172
  web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
46722
47173
  ];
47174
+ let amountToDeposit;
47175
+ let swapInstructions = [];
47176
+ let setupInstructions = [];
47177
+ let swapLookupTables = [];
47178
+ let swapQuote;
47179
+ let sizeConstraintUsed = 0;
46723
47180
  if (depositOpts.depositBank.mint.equals(borrowOpts.borrowBank.mint)) {
46724
- swapResult.push({
46725
- amountToDeposit: borrowOpts.borrowAmount + (depositOpts.loopMode === "DEPOSIT" ? depositOpts.inputDepositAmount : 0),
46726
- swapInstructions: [],
46727
- setupInstructions: [],
46728
- swapLookupTables: []
46729
- });
47181
+ amountToDeposit = borrowOpts.borrowAmount + (depositOpts.loopMode === "DEPOSIT" ? depositOpts.inputDepositAmount : 0);
46730
47182
  } else {
46731
47183
  const destinationTokenAccount = getAssociatedTokenAddressSync(
46732
47184
  new web3_js.PublicKey(depositOpts.depositBank.mint),
@@ -46734,36 +47186,46 @@ async function buildLoopFlashloanTx({
46734
47186
  true,
46735
47187
  depositOpts.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
46736
47188
  );
46737
- const swapResponse = await getJupiterSwapIxsForFlashloan({
46738
- quoteParams: {
46739
- inputMint: borrowOpts.borrowBank.mint.toBase58(),
46740
- outputMint: depositOpts.depositBank.mint.toBase58(),
46741
- amount: uiToNative(borrowOpts.borrowAmount, borrowOpts.borrowBank.mintDecimals).toNumber(),
46742
- dynamicSlippage: swapOpts.jupiterOptions ? swapOpts.jupiterOptions.slippageMode === "DYNAMIC" : true,
46743
- slippageBps: swapOpts.jupiterOptions?.slippageBps ?? void 0,
46744
- swapMode: "ExactIn",
46745
- platformFeeBps: swapOpts.jupiterOptions?.platformFeeBps ?? void 0,
46746
- onlyDirectRoutes: swapOpts.jupiterOptions?.directRoutesOnly ?? false
47189
+ const swapConstraints = await computeFlashloanSwapConstraints({
47190
+ program,
47191
+ marginfiAccount,
47192
+ bankMap,
47193
+ bankMetadataMap,
47194
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
47195
+ primaryIx: {
47196
+ type: "borrow",
47197
+ bank: borrowOpts.borrowBank,
47198
+ tokenProgram: borrowOpts.tokenProgram
47199
+ },
47200
+ secondaryIx: {
47201
+ type: "deposit",
47202
+ bank: depositOpts.depositBank,
47203
+ tokenProgram: depositOpts.tokenProgram
46747
47204
  },
47205
+ overrideInferAccounts
47206
+ });
47207
+ const swapResponse = await getSwapIxsForFlashloan({
47208
+ inputMint: borrowOpts.borrowBank.mint.toBase58(),
47209
+ outputMint: depositOpts.depositBank.mint.toBase58(),
47210
+ amount: uiToNative(borrowOpts.borrowAmount, borrowOpts.borrowBank.mintDecimals).toNumber(),
47211
+ swapMode: "ExactIn",
46748
47212
  authority: marginfiAccount.authority,
46749
47213
  connection,
46750
47214
  destinationTokenAccount,
46751
- configParams: swapOpts.jupiterOptions?.configParams
46752
- });
46753
- swapResponse.forEach((response) => {
46754
- const outAmountThreshold = nativeToUi(
46755
- response.quoteResponse.otherAmountThreshold,
46756
- depositOpts.depositBank.mintDecimals
46757
- );
46758
- const amountToDeposit = outAmountThreshold + (depositOpts.loopMode === "DEPOSIT" ? depositOpts.inputDepositAmount : 0);
46759
- swapResult.push({
46760
- amountToDeposit,
46761
- swapInstructions: [response.swapInstruction],
46762
- setupInstructions: response.setupInstructions,
46763
- swapLookupTables: response.addressLookupTableAddresses,
46764
- quoteResponse: response.quoteResponse
46765
- });
47215
+ swapOpts,
47216
+ sizeConstraint: swapConstraints.sizeConstraint,
47217
+ maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
46766
47218
  });
47219
+ sizeConstraintUsed = swapConstraints.sizeConstraint;
47220
+ const outAmountThreshold = nativeToUi(
47221
+ swapResponse.quoteResponse.otherAmountThreshold,
47222
+ depositOpts.depositBank.mintDecimals
47223
+ );
47224
+ amountToDeposit = outAmountThreshold + (depositOpts.loopMode === "DEPOSIT" ? depositOpts.inputDepositAmount : 0);
47225
+ swapInstructions = swapResponse.swapInstructions;
47226
+ setupInstructions = swapResponse.setupInstructions;
47227
+ swapLookupTables = swapResponse.addressLookupTableAddresses;
47228
+ swapQuote = swapResponse.quoteResponse;
46767
47229
  }
46768
47230
  const borrowIxs = await makeBorrowIx3({
46769
47231
  program,
@@ -46780,140 +47242,136 @@ async function buildLoopFlashloanTx({
46780
47242
  overrideInferAccounts
46781
47243
  }
46782
47244
  });
46783
- for (const [index, item] of swapResult.entries()) {
46784
- const {
46785
- amountToDeposit,
46786
- swapInstructions,
46787
- setupInstructions,
46788
- swapLookupTables,
46789
- quoteResponse
46790
- } = item;
46791
- let depositIxs;
46792
- switch (depositOpts.depositBank.config.assetTag) {
46793
- case 3 /* KAMINO */: {
46794
- const reserve = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.kaminoStates?.reserveState;
46795
- if (!reserve) {
46796
- throw TransactionBuildingError.kaminoReserveNotFound(
46797
- depositOpts.depositBank.address.toBase58(),
46798
- depositOpts.depositBank.mint.toBase58(),
46799
- depositOpts.depositBank.tokenSymbol
46800
- );
46801
- }
46802
- depositIxs = await makeKaminoDepositIx3({
46803
- program,
46804
- bank: depositOpts.depositBank,
46805
- tokenProgram: depositOpts.tokenProgram,
46806
- amount: amountToDeposit,
46807
- accountAddress: marginfiAccount.address,
46808
- authority: marginfiAccount.authority,
46809
- group: marginfiAccount.group,
46810
- reserve,
46811
- opts: {
46812
- wrapAndUnwrapSol: false,
46813
- overrideInferAccounts
46814
- }
46815
- });
46816
- break;
47245
+ let depositIxs;
47246
+ switch (depositOpts.depositBank.config.assetTag) {
47247
+ case 3 /* KAMINO */: {
47248
+ const reserve = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.kaminoStates?.reserveState;
47249
+ if (!reserve) {
47250
+ throw TransactionBuildingError.kaminoReserveNotFound(
47251
+ depositOpts.depositBank.address.toBase58(),
47252
+ depositOpts.depositBank.mint.toBase58(),
47253
+ depositOpts.depositBank.tokenSymbol
47254
+ );
46817
47255
  }
46818
- case 4 /* DRIFT */: {
46819
- const driftState = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.driftStates;
46820
- if (!driftState) {
46821
- throw TransactionBuildingError.driftStateNotFound(
46822
- depositOpts.depositBank.address.toBase58(),
46823
- depositOpts.depositBank.mint.toBase58(),
46824
- depositOpts.depositBank.tokenSymbol
46825
- );
47256
+ depositIxs = await makeKaminoDepositIx3({
47257
+ program,
47258
+ bank: depositOpts.depositBank,
47259
+ tokenProgram: depositOpts.tokenProgram,
47260
+ amount: amountToDeposit,
47261
+ accountAddress: marginfiAccount.address,
47262
+ authority: marginfiAccount.authority,
47263
+ group: marginfiAccount.group,
47264
+ reserve,
47265
+ opts: {
47266
+ wrapAndUnwrapSol: false,
47267
+ overrideInferAccounts
46826
47268
  }
46827
- const driftMarketIndex = driftState.spotMarketState.marketIndex;
46828
- const driftOracle = driftState.spotMarketState.oracle;
46829
- depositIxs = await makeDriftDepositIx3({
46830
- program,
46831
- bank: depositOpts.depositBank,
46832
- tokenProgram: depositOpts.tokenProgram,
46833
- amount: amountToDeposit,
46834
- accountAddress: marginfiAccount.address,
46835
- authority: marginfiAccount.authority,
46836
- group: marginfiAccount.group,
46837
- driftMarketIndex,
46838
- driftOracle,
46839
- opts: {
46840
- wrapAndUnwrapSol: false,
46841
- overrideInferAccounts
46842
- }
46843
- });
46844
- break;
46845
- }
46846
- case 6 /* JUPLEND */: {
46847
- depositIxs = await makeJuplendDepositIx2({
46848
- program,
46849
- bank: depositOpts.depositBank,
46850
- tokenProgram: depositOpts.tokenProgram,
46851
- amount: amountToDeposit,
46852
- accountAddress: marginfiAccount.address,
46853
- authority: marginfiAccount.authority,
46854
- group: marginfiAccount.group,
46855
- opts: {
46856
- wrapAndUnwrapSol: false,
46857
- overrideInferAccounts
46858
- }
46859
- });
46860
- break;
46861
- }
46862
- default: {
46863
- depositIxs = await makeDepositIx3({
46864
- program,
46865
- bank: depositOpts.depositBank,
46866
- tokenProgram: depositOpts.tokenProgram,
46867
- amount: amountToDeposit,
46868
- accountAddress: marginfiAccount.address,
46869
- authority: marginfiAccount.authority,
46870
- group: marginfiAccount.group,
46871
- opts: {
46872
- wrapAndUnwrapSol: false,
46873
- overrideInferAccounts
46874
- }
46875
- });
46876
- break;
46877
- }
47269
+ });
47270
+ break;
46878
47271
  }
46879
- const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
46880
- const flashloanParams = {
46881
- program,
46882
- marginfiAccount,
46883
- bankMap,
46884
- addressLookupTableAccounts: luts,
46885
- blockhash
46886
- };
46887
- const flashloanTx = await makeFlashLoanTx({
46888
- ...flashloanParams,
46889
- ixs: [
46890
- ...cuRequestIxs,
46891
- ...borrowIxs.instructions,
46892
- ...swapInstructions,
46893
- ...depositIxs.instructions
46894
- ]
46895
- });
46896
- const txSize = getTxSize(flashloanTx);
46897
- const keySize = getAccountKeys(flashloanTx, luts);
46898
- const isLast = index === swapResult.length - 1;
46899
- if (txSize > MAX_TX_SIZE || keySize > 64) {
46900
- if (isLast) {
46901
- throw TransactionBuildingError.jupiterSwapSizeExceededLoop(txSize, keySize);
46902
- } else {
46903
- continue;
47272
+ case 4 /* DRIFT */: {
47273
+ const driftState = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.driftStates;
47274
+ if (!driftState) {
47275
+ throw TransactionBuildingError.driftStateNotFound(
47276
+ depositOpts.depositBank.address.toBase58(),
47277
+ depositOpts.depositBank.mint.toBase58(),
47278
+ depositOpts.depositBank.tokenSymbol
47279
+ );
46904
47280
  }
46905
- } else {
46906
- return {
46907
- flashloanTx,
46908
- setupInstructions,
46909
- swapQuote: quoteResponse,
46910
- borrowIxs,
46911
- depositIxs,
46912
- amountToDeposit
46913
- };
47281
+ const driftMarketIndex = driftState.spotMarketState.marketIndex;
47282
+ const driftOracle = driftState.spotMarketState.oracle;
47283
+ depositIxs = await makeDriftDepositIx3({
47284
+ program,
47285
+ bank: depositOpts.depositBank,
47286
+ tokenProgram: depositOpts.tokenProgram,
47287
+ amount: amountToDeposit,
47288
+ accountAddress: marginfiAccount.address,
47289
+ authority: marginfiAccount.authority,
47290
+ group: marginfiAccount.group,
47291
+ driftMarketIndex,
47292
+ driftOracle,
47293
+ opts: {
47294
+ wrapAndUnwrapSol: false,
47295
+ overrideInferAccounts
47296
+ }
47297
+ });
47298
+ break;
47299
+ }
47300
+ case 6 /* JUPLEND */: {
47301
+ depositIxs = await makeJuplendDepositIx2({
47302
+ program,
47303
+ bank: depositOpts.depositBank,
47304
+ tokenProgram: depositOpts.tokenProgram,
47305
+ amount: amountToDeposit,
47306
+ accountAddress: marginfiAccount.address,
47307
+ authority: marginfiAccount.authority,
47308
+ group: marginfiAccount.group,
47309
+ opts: {
47310
+ wrapAndUnwrapSol: false,
47311
+ overrideInferAccounts
47312
+ }
47313
+ });
47314
+ break;
47315
+ }
47316
+ default: {
47317
+ depositIxs = await makeDepositIx3({
47318
+ program,
47319
+ bank: depositOpts.depositBank,
47320
+ tokenProgram: depositOpts.tokenProgram,
47321
+ amount: amountToDeposit,
47322
+ accountAddress: marginfiAccount.address,
47323
+ authority: marginfiAccount.authority,
47324
+ group: marginfiAccount.group,
47325
+ opts: {
47326
+ wrapAndUnwrapSol: false,
47327
+ overrideInferAccounts
47328
+ }
47329
+ });
47330
+ break;
46914
47331
  }
46915
47332
  }
46916
- throw new Error("Failed to build repay with collateral flashloan tx");
47333
+ const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
47334
+ const allNonFlIxs = [
47335
+ ...cuRequestIxs,
47336
+ ...borrowIxs.instructions,
47337
+ ...swapInstructions,
47338
+ ...depositIxs.instructions
47339
+ ];
47340
+ if (swapInstructions.length > 0) {
47341
+ compileFlashloanPrecheck({
47342
+ allIxs: allNonFlIxs,
47343
+ payer: marginfiAccount.authority,
47344
+ luts,
47345
+ sizeConstraint: sizeConstraintUsed,
47346
+ swapIxCount: swapInstructions.length,
47347
+ swapLutCount: swapLookupTables.length
47348
+ });
47349
+ }
47350
+ const flashloanTx = await makeFlashLoanTx({
47351
+ program,
47352
+ marginfiAccount,
47353
+ bankMap,
47354
+ addressLookupTableAccounts: luts,
47355
+ blockhash,
47356
+ ixs: allNonFlIxs
47357
+ });
47358
+ const txSize = getTxSize(flashloanTx);
47359
+ const totalKeys = getTotalAccountKeys(flashloanTx);
47360
+ if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
47361
+ throw TransactionBuildingError.swapSizeExceededLoop(
47362
+ txSize,
47363
+ totalKeys,
47364
+ swapOpts.swapConfig?.provider
47365
+ );
47366
+ }
47367
+ return {
47368
+ flashloanTx,
47369
+ setupInstructions,
47370
+ swapQuote,
47371
+ borrowIxs,
47372
+ depositIxs,
47373
+ amountToDeposit
47374
+ };
46917
47375
  }
46918
47376
  async function makeRepayIx3({
46919
47377
  program,
@@ -47103,18 +47561,18 @@ async function buildRepayWithCollatFlashloanTx({
47103
47561
  overrideInferAccounts,
47104
47562
  blockhash
47105
47563
  }) {
47106
- const swapResult = [];
47107
47564
  const cuRequestIxs = [
47108
47565
  web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
47109
47566
  web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
47110
47567
  ];
47568
+ let amountToRepay;
47569
+ let swapInstructions = [];
47570
+ let setupInstructions = [];
47571
+ let swapLookupTables = [];
47572
+ let swapQuote;
47573
+ let sizeConstraintUsed = 0;
47111
47574
  if (repayOpts.repayBank.mint.equals(withdrawOpts.withdrawBank.mint)) {
47112
- swapResult.push({
47113
- amountToRepay: withdrawOpts.withdrawAmount,
47114
- swapInstructions: [],
47115
- setupInstructions: [],
47116
- swapLookupTables: []
47117
- });
47575
+ amountToRepay = withdrawOpts.withdrawAmount;
47118
47576
  } else {
47119
47577
  const destinationTokenAccount = getAssociatedTokenAddressSync(
47120
47578
  new web3_js.PublicKey(repayOpts.repayBank.mint),
@@ -47122,41 +47580,50 @@ async function buildRepayWithCollatFlashloanTx({
47122
47580
  true,
47123
47581
  repayOpts.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
47124
47582
  );
47125
- const swapResponse = await getJupiterSwapIxsForFlashloan({
47126
- quoteParams: {
47127
- inputMint: withdrawOpts.withdrawBank.mint.toBase58(),
47128
- outputMint: repayOpts.repayBank.mint.toBase58(),
47129
- amount: uiToNative(
47130
- withdrawOpts.withdrawAmount,
47131
- withdrawOpts.withdrawBank.mintDecimals
47132
- ).toNumber(),
47133
- dynamicSlippage: swapOpts.jupiterOptions ? swapOpts.jupiterOptions.slippageMode === "DYNAMIC" : true,
47134
- slippageBps: swapOpts.jupiterOptions?.slippageBps ?? void 0,
47135
- swapMode: "ExactIn",
47136
- platformFeeBps: swapOpts.jupiterOptions?.platformFeeBps ?? void 0,
47137
- onlyDirectRoutes: swapOpts.jupiterOptions?.directRoutesOnly ?? false
47583
+ const swapConstraints = await computeFlashloanSwapConstraints({
47584
+ program,
47585
+ marginfiAccount,
47586
+ bankMap,
47587
+ bankMetadataMap,
47588
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
47589
+ primaryIx: {
47590
+ type: "withdraw",
47591
+ bank: withdrawOpts.withdrawBank,
47592
+ tokenProgram: withdrawOpts.tokenProgram
47593
+ },
47594
+ secondaryIx: {
47595
+ type: "repay",
47596
+ bank: repayOpts.repayBank,
47597
+ tokenProgram: repayOpts.tokenProgram
47138
47598
  },
47599
+ overrideInferAccounts
47600
+ });
47601
+ const swapResponse = await getSwapIxsForFlashloan({
47602
+ inputMint: withdrawOpts.withdrawBank.mint.toBase58(),
47603
+ outputMint: repayOpts.repayBank.mint.toBase58(),
47604
+ amount: uiToNative(
47605
+ withdrawOpts.withdrawAmount,
47606
+ withdrawOpts.withdrawBank.mintDecimals
47607
+ ).toNumber(),
47608
+ swapMode: "ExactIn",
47139
47609
  authority: marginfiAccount.authority,
47140
47610
  connection,
47141
47611
  destinationTokenAccount,
47142
- configParams: swapOpts.jupiterOptions?.configParams
47143
- });
47144
- swapResponse.forEach((response) => {
47145
- const { swapInstruction, addressLookupTableAddresses, quoteResponse } = response;
47146
- const outAmount = nativeToUi(quoteResponse.outAmount, repayOpts.repayBank.mintDecimals);
47147
- const outAmountThreshold = nativeToUi(
47148
- quoteResponse.otherAmountThreshold,
47149
- repayOpts.repayBank.mintDecimals
47150
- );
47151
- const amountToRepay = outAmount > repayOpts.totalPositionAmount ? repayOpts.totalPositionAmount : outAmountThreshold;
47152
- swapResult.push({
47153
- amountToRepay,
47154
- swapInstructions: [swapInstruction],
47155
- setupInstructions: [],
47156
- swapLookupTables: addressLookupTableAddresses,
47157
- quoteResponse
47158
- });
47612
+ swapOpts,
47613
+ sizeConstraint: swapConstraints.sizeConstraint,
47614
+ maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
47159
47615
  });
47616
+ sizeConstraintUsed = swapConstraints.sizeConstraint;
47617
+ const { quoteResponse } = swapResponse;
47618
+ const outAmount = nativeToUi(quoteResponse.outAmount, repayOpts.repayBank.mintDecimals);
47619
+ const outAmountThreshold = nativeToUi(
47620
+ quoteResponse.otherAmountThreshold,
47621
+ repayOpts.repayBank.mintDecimals
47622
+ );
47623
+ amountToRepay = outAmount > repayOpts.totalPositionAmount ? repayOpts.totalPositionAmount : outAmountThreshold;
47624
+ swapInstructions = swapResponse.swapInstructions;
47625
+ swapLookupTables = swapResponse.addressLookupTableAddresses;
47626
+ swapQuote = quoteResponse;
47160
47627
  }
47161
47628
  let withdrawIxs;
47162
47629
  switch (withdrawOpts.withdrawBank.config.assetTag) {
@@ -47294,68 +47761,70 @@ async function buildRepayWithCollatFlashloanTx({
47294
47761
  break;
47295
47762
  }
47296
47763
  }
47297
- for (const [index, item] of swapResult.entries()) {
47298
- const { amountToRepay, swapInstructions, setupInstructions, swapLookupTables, quoteResponse } = item;
47299
- const repayIxs = await makeRepayIx3({
47300
- program,
47301
- bank: repayOpts.repayBank,
47302
- tokenProgram: repayOpts.tokenProgram,
47303
- amount: amountToRepay,
47304
- accountAddress: marginfiAccount.address,
47305
- authority: marginfiAccount.authority,
47306
- repayAll: isWholePosition(
47307
- {
47308
- amount: repayOpts.totalPositionAmount,
47309
- isLending: true
47310
- },
47311
- amountToRepay,
47312
- repayOpts.repayBank.mintDecimals
47313
- ),
47314
- isSync: false,
47315
- opts: {
47316
- wrapAndUnwrapSol: false,
47317
- overrideInferAccounts
47318
- }
47319
- });
47320
- const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
47321
- const flashloanParams = {
47322
- program,
47323
- marginfiAccount,
47324
- bankMap,
47325
- addressLookupTableAccounts: luts,
47326
- blockhash
47327
- };
47328
- const flashloanTx = await makeFlashLoanTx({
47329
- ...flashloanParams,
47330
- ixs: [
47331
- ...cuRequestIxs,
47332
- ...withdrawIxs.instructions,
47333
- ...swapInstructions,
47334
- ...repayIxs.instructions
47335
- ],
47336
- isSync: true
47337
- });
47338
- const txSize = getTxSize(flashloanTx);
47339
- const keySize = getAccountKeys(flashloanTx, luts);
47340
- const isLast = index === swapResult.length - 1;
47341
- if (txSize > MAX_TX_SIZE || keySize > 64) {
47342
- if (isLast) {
47343
- throw TransactionBuildingError.jupiterSwapSizeExceededRepay(txSize, keySize);
47344
- } else {
47345
- continue;
47346
- }
47347
- } else {
47348
- return {
47349
- flashloanTx,
47350
- setupInstructions,
47351
- swapQuote: quoteResponse,
47352
- withdrawIxs,
47353
- repayIxs,
47354
- amountToRepay
47355
- };
47764
+ const repayIxs = await makeRepayIx3({
47765
+ program,
47766
+ bank: repayOpts.repayBank,
47767
+ tokenProgram: repayOpts.tokenProgram,
47768
+ amount: amountToRepay,
47769
+ accountAddress: marginfiAccount.address,
47770
+ authority: marginfiAccount.authority,
47771
+ repayAll: isWholePosition(
47772
+ {
47773
+ amount: repayOpts.totalPositionAmount,
47774
+ isLending: true
47775
+ },
47776
+ amountToRepay,
47777
+ repayOpts.repayBank.mintDecimals
47778
+ ),
47779
+ isSync: false,
47780
+ opts: {
47781
+ wrapAndUnwrapSol: false,
47782
+ overrideInferAccounts
47356
47783
  }
47784
+ });
47785
+ const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
47786
+ const allNonFlIxs = [
47787
+ ...cuRequestIxs,
47788
+ ...withdrawIxs.instructions,
47789
+ ...swapInstructions,
47790
+ ...repayIxs.instructions
47791
+ ];
47792
+ if (swapInstructions.length > 0) {
47793
+ compileFlashloanPrecheck({
47794
+ allIxs: allNonFlIxs,
47795
+ payer: marginfiAccount.authority,
47796
+ luts,
47797
+ sizeConstraint: sizeConstraintUsed,
47798
+ swapIxCount: swapInstructions.length,
47799
+ swapLutCount: swapLookupTables.length
47800
+ });
47357
47801
  }
47358
- throw new Error("Failed to build repay with collateral flashloan tx");
47802
+ const flashloanTx = await makeFlashLoanTx({
47803
+ program,
47804
+ marginfiAccount,
47805
+ bankMap,
47806
+ addressLookupTableAccounts: luts,
47807
+ blockhash,
47808
+ ixs: allNonFlIxs,
47809
+ isSync: true
47810
+ });
47811
+ const txSize = getTxSize(flashloanTx);
47812
+ const totalKeys = getTotalAccountKeys(flashloanTx);
47813
+ if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
47814
+ throw TransactionBuildingError.swapSizeExceededRepay(
47815
+ txSize,
47816
+ totalKeys,
47817
+ swapOpts.swapConfig?.provider
47818
+ );
47819
+ }
47820
+ return {
47821
+ flashloanTx,
47822
+ setupInstructions,
47823
+ swapQuote,
47824
+ withdrawIxs,
47825
+ repayIxs,
47826
+ amountToRepay
47827
+ };
47359
47828
  }
47360
47829
 
47361
47830
  // src/services/account/actions/emissions.ts
@@ -47509,11 +47978,16 @@ async function buildSwapCollateralFlashloanTx({
47509
47978
  actualWithdrawAmount,
47510
47979
  withdrawBank.mintDecimals
47511
47980
  );
47512
- const swapResult = [];
47513
47981
  const cuRequestIxs = [
47514
47982
  web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
47515
47983
  web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
47516
47984
  ];
47985
+ let amountToDeposit;
47986
+ let swapInstructions = [];
47987
+ let setupInstructions = [];
47988
+ let swapLookupTables = [];
47989
+ let swapQuote;
47990
+ let sizeConstraintUsed = 0;
47517
47991
  let withdrawIxs;
47518
47992
  switch (withdrawOpts.withdrawBank.config.assetTag) {
47519
47993
  case 3 /* KAMINO */: {
@@ -47623,12 +48097,7 @@ async function buildSwapCollateralFlashloanTx({
47623
48097
  }
47624
48098
  }
47625
48099
  if (depositBank.mint.equals(withdrawBank.mint)) {
47626
- swapResult.push({
47627
- amountToDeposit: actualWithdrawAmount,
47628
- swapInstructions: [],
47629
- setupInstructions: [],
47630
- swapLookupTables: []
47631
- });
48100
+ amountToDeposit = actualWithdrawAmount;
47632
48101
  } else {
47633
48102
  const destinationTokenAccount = getAssociatedTokenAddressSync(
47634
48103
  depositBank.mint,
@@ -47636,175 +48105,168 @@ async function buildSwapCollateralFlashloanTx({
47636
48105
  true,
47637
48106
  depositTokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
47638
48107
  );
47639
- const swapResponses = await getJupiterSwapIxsForFlashloan({
47640
- quoteParams: {
47641
- inputMint: withdrawBank.mint.toBase58(),
47642
- outputMint: depositBank.mint.toBase58(),
47643
- amount: uiToNative(actualWithdrawAmount, withdrawBank.mintDecimals).toNumber(),
47644
- dynamicSlippage: swapOpts.jupiterOptions ? swapOpts.jupiterOptions.slippageMode === "DYNAMIC" : true,
47645
- slippageBps: swapOpts.jupiterOptions?.slippageBps,
47646
- swapMode: "ExactIn",
47647
- platformFeeBps: swapOpts.jupiterOptions?.platformFeeBps,
47648
- onlyDirectRoutes: swapOpts.jupiterOptions?.directRoutesOnly ?? false
47649
- },
48108
+ const swapConstraints = await computeFlashloanSwapConstraints({
48109
+ program,
48110
+ marginfiAccount,
48111
+ bankMap,
48112
+ bankMetadataMap,
48113
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
48114
+ primaryIx: { type: "withdraw", bank: withdrawBank, tokenProgram: withdrawTokenProgram },
48115
+ secondaryIx: { type: "deposit", bank: depositBank, tokenProgram: depositTokenProgram },
48116
+ overrideInferAccounts
48117
+ });
48118
+ const swapResponses = await getSwapIxsForFlashloan({
48119
+ inputMint: withdrawBank.mint.toBase58(),
48120
+ outputMint: depositBank.mint.toBase58(),
48121
+ amount: uiToNative(actualWithdrawAmount, withdrawBank.mintDecimals).toNumber(),
48122
+ swapMode: "ExactIn",
47650
48123
  authority: marginfiAccount.authority,
47651
48124
  connection,
47652
48125
  destinationTokenAccount,
47653
- configParams: swapOpts.jupiterOptions?.configParams
47654
- });
47655
- swapResponses.forEach((response) => {
47656
- const outAmountThreshold = nativeToUi(
47657
- response.quoteResponse.otherAmountThreshold,
47658
- depositBank.mintDecimals
47659
- );
47660
- swapResult.push({
47661
- amountToDeposit: outAmountThreshold,
47662
- swapInstructions: [response.swapInstruction],
47663
- setupInstructions: response.setupInstructions,
47664
- swapLookupTables: response.addressLookupTableAddresses,
47665
- quoteResponse: response.quoteResponse
47666
- });
48126
+ swapOpts,
48127
+ sizeConstraint: swapConstraints.sizeConstraint,
48128
+ maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
47667
48129
  });
47668
- }
47669
- if (swapResult.length === 0) {
47670
- throw new Error(
47671
- `No swap routes found for ${withdrawBank.mint.toBase58()} -> ${depositBank.mint.toBase58()}`
48130
+ sizeConstraintUsed = swapConstraints.sizeConstraint;
48131
+ amountToDeposit = nativeToUi(
48132
+ swapResponses.quoteResponse.otherAmountThreshold,
48133
+ depositBank.mintDecimals
47672
48134
  );
48135
+ swapInstructions = swapResponses.swapInstructions;
48136
+ setupInstructions = swapResponses.setupInstructions;
48137
+ swapLookupTables = swapResponses.addressLookupTableAddresses;
48138
+ swapQuote = swapResponses.quoteResponse;
47673
48139
  }
47674
- for (const [index, item] of swapResult.entries()) {
47675
- const {
47676
- amountToDeposit,
47677
- swapInstructions,
47678
- setupInstructions,
47679
- swapLookupTables,
47680
- quoteResponse
47681
- } = item;
47682
- let depositIxs;
47683
- switch (depositBank.config.assetTag) {
47684
- case 3 /* KAMINO */: {
47685
- const reserve = bankMetadataMap[depositBank.address.toBase58()]?.kaminoStates?.reserveState;
47686
- if (!reserve) {
47687
- throw TransactionBuildingError.kaminoReserveNotFound(
47688
- depositBank.address.toBase58(),
47689
- depositBank.mint.toBase58(),
47690
- depositBank.tokenSymbol
47691
- );
47692
- }
47693
- depositIxs = await makeKaminoDepositIx3({
47694
- program,
47695
- bank: depositBank,
47696
- tokenProgram: depositTokenProgram,
47697
- amount: amountToDeposit,
47698
- accountAddress: marginfiAccount.address,
47699
- authority: marginfiAccount.authority,
47700
- group: marginfiAccount.group,
47701
- reserve,
47702
- opts: {
47703
- wrapAndUnwrapSol: false,
47704
- overrideInferAccounts
47705
- }
47706
- });
47707
- break;
48140
+ let depositIxs;
48141
+ switch (depositBank.config.assetTag) {
48142
+ case 3 /* KAMINO */: {
48143
+ const reserve = bankMetadataMap[depositBank.address.toBase58()]?.kaminoStates?.reserveState;
48144
+ if (!reserve) {
48145
+ throw TransactionBuildingError.kaminoReserveNotFound(
48146
+ depositBank.address.toBase58(),
48147
+ depositBank.mint.toBase58(),
48148
+ depositBank.tokenSymbol
48149
+ );
47708
48150
  }
47709
- case 4 /* DRIFT */: {
47710
- const driftState = bankMetadataMap[depositBank.address.toBase58()]?.driftStates;
47711
- if (!driftState) {
47712
- throw TransactionBuildingError.driftStateNotFound(
47713
- depositBank.address.toBase58(),
47714
- depositBank.mint.toBase58(),
47715
- depositBank.tokenSymbol
47716
- );
48151
+ depositIxs = await makeKaminoDepositIx3({
48152
+ program,
48153
+ bank: depositBank,
48154
+ tokenProgram: depositTokenProgram,
48155
+ amount: amountToDeposit,
48156
+ accountAddress: marginfiAccount.address,
48157
+ authority: marginfiAccount.authority,
48158
+ group: marginfiAccount.group,
48159
+ reserve,
48160
+ opts: {
48161
+ wrapAndUnwrapSol: false,
48162
+ overrideInferAccounts
47717
48163
  }
47718
- const driftMarketIndex = driftState.spotMarketState.marketIndex;
47719
- const driftOracle = driftState.spotMarketState.oracle;
47720
- depositIxs = await makeDriftDepositIx3({
47721
- program,
47722
- bank: depositBank,
47723
- tokenProgram: depositTokenProgram,
47724
- amount: amountToDeposit,
47725
- accountAddress: marginfiAccount.address,
47726
- authority: marginfiAccount.authority,
47727
- group: marginfiAccount.group,
47728
- driftMarketIndex,
47729
- driftOracle,
47730
- opts: {
47731
- wrapAndUnwrapSol: false,
47732
- overrideInferAccounts
47733
- }
47734
- });
47735
- break;
47736
- }
47737
- case 6 /* JUPLEND */: {
47738
- depositIxs = await makeJuplendDepositIx2({
47739
- program,
47740
- bank: depositBank,
47741
- tokenProgram: depositTokenProgram,
47742
- amount: amountToDeposit,
47743
- accountAddress: marginfiAccount.address,
47744
- authority: marginfiAccount.authority,
47745
- group: marginfiAccount.group,
47746
- opts: {
47747
- wrapAndUnwrapSol: false,
47748
- overrideInferAccounts
47749
- }
47750
- });
47751
- break;
47752
- }
47753
- default: {
47754
- depositIxs = await makeDepositIx3({
47755
- program,
47756
- bank: depositBank,
47757
- tokenProgram: depositTokenProgram,
47758
- amount: amountToDeposit,
47759
- accountAddress: marginfiAccount.address,
47760
- authority: marginfiAccount.authority,
47761
- group: marginfiAccount.group,
47762
- opts: {
47763
- wrapAndUnwrapSol: false,
47764
- overrideInferAccounts
47765
- }
47766
- });
47767
- break;
47768
- }
48164
+ });
48165
+ break;
47769
48166
  }
47770
- const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
47771
- const flashloanParams = {
47772
- program,
47773
- marginfiAccount,
47774
- bankMap,
47775
- addressLookupTableAccounts: luts,
47776
- blockhash
47777
- };
47778
- const flashloanTx = await makeFlashLoanTx({
47779
- ...flashloanParams,
47780
- ixs: [
47781
- ...cuRequestIxs,
47782
- ...withdrawIxs.instructions,
47783
- ...swapInstructions,
47784
- ...depositIxs.instructions
47785
- ],
47786
- isSync: true
47787
- });
47788
- const txSize = getTxSize(flashloanTx);
47789
- const keySize = getAccountKeys(flashloanTx, luts);
47790
- const isLast = index === swapResult.length - 1;
47791
- if (txSize > MAX_TX_SIZE || keySize > 64) {
47792
- if (isLast) {
47793
- throw TransactionBuildingError.jupiterSwapSizeExceededLoop(txSize, keySize);
47794
- } else {
47795
- continue;
48167
+ case 4 /* DRIFT */: {
48168
+ const driftState = bankMetadataMap[depositBank.address.toBase58()]?.driftStates;
48169
+ if (!driftState) {
48170
+ throw TransactionBuildingError.driftStateNotFound(
48171
+ depositBank.address.toBase58(),
48172
+ depositBank.mint.toBase58(),
48173
+ depositBank.tokenSymbol
48174
+ );
47796
48175
  }
47797
- } else {
47798
- return {
47799
- flashloanTx,
47800
- setupInstructions,
47801
- swapQuote: quoteResponse,
47802
- withdrawIxs,
47803
- depositIxs
47804
- };
48176
+ const driftMarketIndex = driftState.spotMarketState.marketIndex;
48177
+ const driftOracle = driftState.spotMarketState.oracle;
48178
+ depositIxs = await makeDriftDepositIx3({
48179
+ program,
48180
+ bank: depositBank,
48181
+ tokenProgram: depositTokenProgram,
48182
+ amount: amountToDeposit,
48183
+ accountAddress: marginfiAccount.address,
48184
+ authority: marginfiAccount.authority,
48185
+ group: marginfiAccount.group,
48186
+ driftMarketIndex,
48187
+ driftOracle,
48188
+ opts: {
48189
+ wrapAndUnwrapSol: false,
48190
+ overrideInferAccounts
48191
+ }
48192
+ });
48193
+ break;
48194
+ }
48195
+ case 6 /* JUPLEND */: {
48196
+ depositIxs = await makeJuplendDepositIx2({
48197
+ program,
48198
+ bank: depositBank,
48199
+ tokenProgram: depositTokenProgram,
48200
+ amount: amountToDeposit,
48201
+ accountAddress: marginfiAccount.address,
48202
+ authority: marginfiAccount.authority,
48203
+ group: marginfiAccount.group,
48204
+ opts: {
48205
+ wrapAndUnwrapSol: false,
48206
+ overrideInferAccounts
48207
+ }
48208
+ });
48209
+ break;
47805
48210
  }
48211
+ default: {
48212
+ depositIxs = await makeDepositIx3({
48213
+ program,
48214
+ bank: depositBank,
48215
+ tokenProgram: depositTokenProgram,
48216
+ amount: amountToDeposit,
48217
+ accountAddress: marginfiAccount.address,
48218
+ authority: marginfiAccount.authority,
48219
+ group: marginfiAccount.group,
48220
+ opts: {
48221
+ wrapAndUnwrapSol: false,
48222
+ overrideInferAccounts
48223
+ }
48224
+ });
48225
+ break;
48226
+ }
48227
+ }
48228
+ const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
48229
+ const allNonFlIxs = [
48230
+ ...cuRequestIxs,
48231
+ ...withdrawIxs.instructions,
48232
+ ...swapInstructions,
48233
+ ...depositIxs.instructions
48234
+ ];
48235
+ if (swapInstructions.length > 0) {
48236
+ compileFlashloanPrecheck({
48237
+ allIxs: allNonFlIxs,
48238
+ payer: marginfiAccount.authority,
48239
+ luts,
48240
+ sizeConstraint: sizeConstraintUsed,
48241
+ swapIxCount: swapInstructions.length,
48242
+ swapLutCount: swapLookupTables.length
48243
+ });
47806
48244
  }
47807
- throw new Error("Failed to build swap collateral flashloan tx");
48245
+ const flashloanTx = await makeFlashLoanTx({
48246
+ program,
48247
+ marginfiAccount,
48248
+ bankMap,
48249
+ addressLookupTableAccounts: luts,
48250
+ blockhash,
48251
+ ixs: allNonFlIxs,
48252
+ isSync: true
48253
+ });
48254
+ const txSize = getTxSize(flashloanTx);
48255
+ const totalKeys = getTotalAccountKeys(flashloanTx);
48256
+ if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
48257
+ throw TransactionBuildingError.swapSizeExceededPositionSwap(
48258
+ txSize,
48259
+ totalKeys,
48260
+ swapOpts.swapConfig?.provider
48261
+ );
48262
+ }
48263
+ return {
48264
+ flashloanTx,
48265
+ setupInstructions,
48266
+ swapQuote,
48267
+ withdrawIxs,
48268
+ depositIxs
48269
+ };
47808
48270
  }
47809
48271
  async function makeSwapDebtTx(params) {
47810
48272
  const {
@@ -47942,7 +48404,6 @@ async function buildSwapDebtFlashloanTx({
47942
48404
  throw new Error("repayAmount must be greater than 0");
47943
48405
  }
47944
48406
  const actualRepayAmount = Math.min(repayAmount ?? totalPositionAmount, totalPositionAmount);
47945
- const swapResult = [];
47946
48407
  const cuRequestIxs = [
47947
48408
  web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
47948
48409
  web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
@@ -47953,313 +48414,120 @@ async function buildSwapDebtFlashloanTx({
47953
48414
  true,
47954
48415
  repayTokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
47955
48416
  );
47956
- const jupiterApiClient = swapOpts.jupiterOptions?.configParams?.basePath ? new api.SwapApi(new api.Configuration(swapOpts.jupiterOptions.configParams)) : api.createJupiterApiClient(swapOpts.jupiterOptions?.configParams);
47957
- const estimateQuote = await jupiterApiClient.quoteGet({
48417
+ const { otherAmountThreshold } = await getExactOutEstimate({
47958
48418
  inputMint: borrowBank.mint.toBase58(),
47959
48419
  outputMint: repayBank.mint.toBase58(),
47960
48420
  amount: uiToNative(actualRepayAmount, repayBank.mintDecimals).toNumber(),
47961
- swapMode: "ExactOut",
47962
- dynamicSlippage: swapOpts.jupiterOptions ? swapOpts.jupiterOptions.slippageMode === "DYNAMIC" : true,
47963
- slippageBps: swapOpts.jupiterOptions?.slippageBps
48421
+ swapOpts,
48422
+ connection
47964
48423
  });
47965
- const estimatedBorrowAmount = nativeToUi(
47966
- estimateQuote.otherAmountThreshold,
47967
- borrowBank.mintDecimals
47968
- );
47969
- const swapResponses = await getJupiterSwapIxsForFlashloan({
47970
- quoteParams: {
47971
- inputMint: borrowBank.mint.toBase58(),
47972
- outputMint: repayBank.mint.toBase58(),
47973
- amount: uiToNative(estimatedBorrowAmount, borrowBank.mintDecimals).toNumber(),
47974
- dynamicSlippage: swapOpts.jupiterOptions ? swapOpts.jupiterOptions.slippageMode === "DYNAMIC" : true,
47975
- slippageBps: swapOpts.jupiterOptions?.slippageBps,
47976
- swapMode: "ExactIn",
47977
- platformFeeBps: swapOpts.jupiterOptions?.platformFeeBps,
47978
- onlyDirectRoutes: swapOpts.jupiterOptions?.directRoutesOnly ?? false
47979
- },
48424
+ const estimatedBorrowAmount = nativeToUi(otherAmountThreshold, borrowBank.mintDecimals);
48425
+ const swapConstraints = await computeFlashloanSwapConstraints({
48426
+ program,
48427
+ marginfiAccount,
48428
+ bankMap,
48429
+ bankMetadataMap,
48430
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
48431
+ primaryIx: { type: "borrow", bank: borrowBank, tokenProgram: borrowTokenProgram },
48432
+ secondaryIx: { type: "repay", bank: repayBank, tokenProgram: repayTokenProgram },
48433
+ overrideInferAccounts
48434
+ });
48435
+ const swapResponses = await getSwapIxsForFlashloan({
48436
+ inputMint: borrowBank.mint.toBase58(),
48437
+ outputMint: repayBank.mint.toBase58(),
48438
+ amount: uiToNative(estimatedBorrowAmount, borrowBank.mintDecimals).toNumber(),
48439
+ swapMode: "ExactIn",
47980
48440
  authority: marginfiAccount.authority,
47981
48441
  connection,
47982
48442
  destinationTokenAccount,
47983
- configParams: swapOpts.jupiterOptions?.configParams
48443
+ swapOpts,
48444
+ sizeConstraint: swapConstraints.sizeConstraint,
48445
+ maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
47984
48446
  });
47985
- swapResponses.forEach((response) => {
47986
- const outAmount = nativeToUi(response.quoteResponse.outAmount, repayBank.mintDecimals);
47987
- const outAmountThreshold = nativeToUi(
47988
- response.quoteResponse.otherAmountThreshold,
47989
- repayBank.mintDecimals
47990
- );
47991
- const amountToRepay = outAmount > totalPositionAmount ? totalPositionAmount : outAmountThreshold;
47992
- const borrowAmount = nativeToUi(response.quoteResponse.inAmount, borrowBank.mintDecimals);
47993
- swapResult.push({
47994
- amountToRepay,
47995
- borrowAmount,
47996
- swapInstructions: [response.swapInstruction],
47997
- setupInstructions: response.setupInstructions,
47998
- swapLookupTables: response.addressLookupTableAddresses,
47999
- quoteResponse: response.quoteResponse
48000
- });
48447
+ const { quoteResponse } = swapResponses;
48448
+ const outAmount = nativeToUi(quoteResponse.outAmount, repayBank.mintDecimals);
48449
+ const outAmountThreshold = nativeToUi(quoteResponse.otherAmountThreshold, repayBank.mintDecimals);
48450
+ const amountToRepay = outAmount > totalPositionAmount ? totalPositionAmount : outAmountThreshold;
48451
+ const borrowAmount = nativeToUi(quoteResponse.inAmount, borrowBank.mintDecimals);
48452
+ const borrowIxs = await makeBorrowIx3({
48453
+ program,
48454
+ bank: borrowBank,
48455
+ bankMap,
48456
+ tokenProgram: borrowTokenProgram,
48457
+ amount: borrowAmount,
48458
+ marginfiAccount,
48459
+ authority: marginfiAccount.authority,
48460
+ isSync: true,
48461
+ opts: {
48462
+ createAtas: false,
48463
+ wrapAndUnwrapSol: false,
48464
+ overrideInferAccounts
48465
+ }
48001
48466
  });
48002
- if (swapResult.length === 0) {
48003
- throw new Error(
48004
- `No swap routes found for ${borrowBank.mint.toBase58()} -> ${repayBank.mint.toBase58()}`
48005
- );
48006
- }
48007
- for (const [index, item] of swapResult.entries()) {
48008
- const {
48467
+ const repayIxs = await makeRepayIx3({
48468
+ program,
48469
+ bank: repayBank,
48470
+ tokenProgram: repayTokenProgram,
48471
+ amount: amountToRepay,
48472
+ accountAddress: marginfiAccount.address,
48473
+ authority: marginfiAccount.authority,
48474
+ repayAll: isWholePosition(
48475
+ {
48476
+ amount: totalPositionAmount,
48477
+ isLending: false
48478
+ },
48009
48479
  amountToRepay,
48010
- borrowAmount,
48011
- swapInstructions,
48012
- setupInstructions,
48013
- swapLookupTables,
48014
- quoteResponse
48015
- } = item;
48016
- const borrowIxs = await makeBorrowIx3({
48017
- program,
48018
- bank: borrowBank,
48019
- bankMap,
48020
- tokenProgram: borrowTokenProgram,
48021
- amount: borrowAmount,
48022
- marginfiAccount,
48023
- authority: marginfiAccount.authority,
48024
- isSync: true,
48025
- opts: {
48026
- createAtas: false,
48027
- wrapAndUnwrapSol: false,
48028
- overrideInferAccounts
48029
- }
48030
- });
48031
- const repayIxs = await makeRepayIx3({
48032
- program,
48033
- bank: repayBank,
48034
- tokenProgram: repayTokenProgram,
48035
- amount: amountToRepay,
48036
- accountAddress: marginfiAccount.address,
48037
- authority: marginfiAccount.authority,
48038
- repayAll: isWholePosition(
48039
- {
48040
- amount: totalPositionAmount,
48041
- isLending: false
48042
- },
48043
- amountToRepay,
48044
- repayBank.mintDecimals
48045
- ),
48046
- isSync: true,
48047
- opts: {
48048
- wrapAndUnwrapSol: false,
48049
- overrideInferAccounts
48050
- }
48051
- });
48052
- const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
48053
- const flashloanParams = {
48054
- program,
48055
- marginfiAccount,
48056
- bankMap,
48057
- addressLookupTableAccounts: luts,
48058
- blockhash
48059
- };
48060
- const flashloanTx = await makeFlashLoanTx({
48061
- ...flashloanParams,
48062
- ixs: [
48063
- ...cuRequestIxs,
48064
- ...borrowIxs.instructions,
48065
- ...swapInstructions,
48066
- ...repayIxs.instructions
48067
- ],
48068
- isSync: true
48069
- });
48070
- const txSize = getTxSize(flashloanTx);
48071
- const keySize = getAccountKeys(flashloanTx, luts);
48072
- const isLast = index === swapResult.length - 1;
48073
- if (txSize > MAX_TX_SIZE || keySize > 64) {
48074
- if (isLast) {
48075
- throw TransactionBuildingError.jupiterSwapSizeExceededLoop(txSize, keySize);
48076
- } else {
48077
- continue;
48078
- }
48079
- } else {
48080
- return {
48081
- flashloanTx,
48082
- setupInstructions,
48083
- swapQuote: quoteResponse,
48084
- borrowIxs,
48085
- repayIxs
48086
- };
48087
- }
48088
- }
48089
- throw new Error("Failed to build swap debt flashloan tx");
48090
- }
48091
- var SYSVAR_CLOCK_ID2 = new web3_js.PublicKey("SysvarC1ock11111111111111111111111111111111");
48092
- async function makeMintStakedLstIx(params) {
48093
- const { amount, authority, stakeAccountPk, validator, connection } = params;
48094
- const pool = findPoolAddress(validator);
48095
- const lstMint = findPoolMintAddress(pool);
48096
- const poolStakeAuth = findPoolStakeAuthorityAddress(pool);
48097
- const lstAta = getAssociatedTokenAddressSync(lstMint, authority);
48098
- const [lstAccInfo, stakeAccInfoParsed, rentExemptReserve] = await Promise.all([
48099
- connection.getAccountInfo(lstAta),
48100
- connection.getParsedAccountInfo(stakeAccountPk),
48101
- connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space)
48102
- ]);
48103
- const stakeAccParsed = stakeAccInfoParsed?.value?.data;
48104
- const amountLamports = Math.round(Number(amount) * web3_js.LAMPORTS_PER_SOL);
48105
- const stakeAccLamports = Number(stakeAccParsed?.parsed?.info?.stake?.delegation?.stake ?? 0);
48106
- const isFullStake = amountLamports >= stakeAccLamports;
48107
- const instructions2 = [];
48108
- const signers = [];
48109
- if (!lstAccInfo) {
48110
- instructions2.push(
48111
- createAssociatedTokenAccountInstruction(authority, lstAta, authority, lstMint)
48112
- );
48113
- }
48114
- let targetStakePubkey;
48115
- if (!isFullStake) {
48116
- const splitStakeAccount = web3_js.Keypair.generate();
48117
- signers.push(splitStakeAccount);
48118
- targetStakePubkey = splitStakeAccount.publicKey;
48119
- instructions2.push(
48120
- ...web3_js.StakeProgram.split(
48121
- {
48122
- stakePubkey: stakeAccountPk,
48123
- authorizedPubkey: authority,
48124
- splitStakePubkey: splitStakeAccount.publicKey,
48125
- lamports: amountLamports
48126
- },
48127
- rentExemptReserve
48128
- ).instructions
48129
- );
48130
- } else {
48131
- targetStakePubkey = stakeAccountPk;
48132
- }
48133
- const [authorizeStakerIx, authorizeWithdrawIx] = await Promise.all([
48134
- web3_js.StakeProgram.authorize({
48135
- stakePubkey: targetStakePubkey,
48136
- authorizedPubkey: authority,
48137
- newAuthorizedPubkey: poolStakeAuth,
48138
- stakeAuthorizationType: web3_js.StakeAuthorizationLayout.Staker
48139
- }).instructions,
48140
- web3_js.StakeProgram.authorize({
48141
- stakePubkey: targetStakePubkey,
48142
- authorizedPubkey: authority,
48143
- newAuthorizedPubkey: poolStakeAuth,
48144
- stakeAuthorizationType: web3_js.StakeAuthorizationLayout.Withdrawer
48145
- }).instructions
48146
- ]);
48147
- [authorizeStakerIx[0], authorizeWithdrawIx[0]].forEach((ix) => {
48148
- if (ix) {
48149
- ix.keys = ix.keys.map((key) => ({
48150
- ...key,
48151
- isWritable: key.pubkey.equals(SYSVAR_CLOCK_ID2) ? false : key.isWritable
48152
- }));
48480
+ repayBank.mintDecimals
48481
+ ),
48482
+ isSync: true,
48483
+ opts: {
48484
+ wrapAndUnwrapSol: false,
48485
+ overrideInferAccounts
48153
48486
  }
48154
48487
  });
48155
- instructions2.push(...authorizeStakerIx, ...authorizeWithdrawIx);
48156
- const depositStakeIx = await SinglePoolInstruction.depositStake(
48157
- pool,
48158
- targetStakePubkey,
48159
- lstAta,
48160
- authority
48161
- );
48162
- instructions2.push(depositStakeIx);
48163
- return { instructions: instructions2, keys: signers };
48164
- }
48165
- async function makeMintStakedLstTx(params) {
48166
- const { connection, luts, blockhash: providedBlockhash } = params;
48167
- const { instructions: instructions2, keys } = await makeMintStakedLstIx(params);
48168
- const blockhash = providedBlockhash ?? (await connection.getLatestBlockhash("confirmed")).blockhash;
48169
- const message = new web3_js.TransactionMessage({
48170
- payerKey: params.authority,
48171
- recentBlockhash: blockhash,
48172
- instructions: instructions2
48173
- }).compileToV0Message(luts);
48174
- const tx = new web3_js.VersionedTransaction(message);
48175
- return addTransactionMetadata(tx, {
48176
- signers: keys,
48177
- addressLookupTables: luts,
48178
- type: "DEPOSIT_STAKE" /* DEPOSIT_STAKE */
48179
- });
48180
- }
48181
- async function makeRedeemStakedLstIx(params) {
48182
- const { amount, authority, validator, connection } = params;
48183
- const pool = findPoolAddress(validator);
48184
- const lstMint = findPoolMintAddress(pool);
48185
- const mintAuthority = findPoolMintAuthorityAddress(pool);
48186
- const lstAta = getAssociatedTokenAddressSync(lstMint, authority);
48187
- const rentExemption = await connection.getMinimumBalanceForRentExemption(
48188
- web3_js.StakeProgram.space
48189
- );
48190
- const stakeAmount = new BigNumber3__default.default(new BigNumber3__default.default(amount).toString());
48191
- const instructions2 = [];
48192
- const signers = [];
48193
- const stakeAccount = web3_js.Keypair.generate();
48194
- signers.push(stakeAccount);
48195
- instructions2.push(
48196
- web3_js.SystemProgram.createAccount({
48197
- fromPubkey: authority,
48198
- newAccountPubkey: stakeAccount.publicKey,
48199
- lamports: rentExemption,
48200
- space: web3_js.StakeProgram.space,
48201
- programId: web3_js.StakeProgram.programId
48202
- })
48203
- );
48204
- instructions2.push(
48205
- createApproveInstruction(
48206
- lstAta,
48207
- mintAuthority,
48208
- authority,
48209
- BigInt(stakeAmount.multipliedBy(1e9).toFixed(0))
48210
- )
48211
- );
48212
- const withdrawStakeIx = await SinglePoolInstruction.withdrawStake(
48213
- pool,
48214
- stakeAccount.publicKey,
48215
- authority,
48216
- lstAta,
48217
- stakeAmount
48218
- );
48219
- instructions2.push(withdrawStakeIx);
48220
- return { instructions: instructions2, keys: signers };
48221
- }
48222
- async function makeRedeemStakedLstTx(params) {
48223
- const { connection, luts, blockhash: providedBlockhash } = params;
48224
- const { instructions: instructions2, keys } = await makeRedeemStakedLstIx(params);
48225
- const blockhash = providedBlockhash ?? (await connection.getLatestBlockhash("confirmed")).blockhash;
48226
- const message = new web3_js.TransactionMessage({
48227
- payerKey: params.authority,
48228
- recentBlockhash: blockhash,
48229
- instructions: instructions2
48230
- }).compileToV0Message(luts);
48231
- const tx = new web3_js.VersionedTransaction(message);
48232
- return addTransactionMetadata(tx, {
48233
- signers: keys,
48234
- addressLookupTables: luts,
48235
- type: "WITHDRAW_STAKE" /* WITHDRAW_STAKE */
48236
- });
48237
- }
48238
- async function makeMergeStakeAccountsTx(params) {
48239
- const {
48240
- authority,
48241
- sourceStakeAccount,
48242
- destinationStakeAccount,
48243
- connection,
48488
+ const luts = [
48489
+ ...addressLookupTableAccounts ?? [],
48490
+ ...swapResponses.addressLookupTableAddresses
48491
+ ];
48492
+ const allNonFlIxs = [
48493
+ ...cuRequestIxs,
48494
+ ...borrowIxs.instructions,
48495
+ ...swapResponses.swapInstructions,
48496
+ ...repayIxs.instructions
48497
+ ];
48498
+ compileFlashloanPrecheck({
48499
+ allIxs: allNonFlIxs,
48500
+ payer: marginfiAccount.authority,
48244
48501
  luts,
48245
- blockhash: providedBlockhash
48246
- } = params;
48247
- const mergeIx = web3_js.StakeProgram.merge({
48248
- stakePubkey: destinationStakeAccount,
48249
- sourceStakePubKey: sourceStakeAccount,
48250
- authorizedPubkey: authority
48251
- }).instructions;
48252
- const blockhash = providedBlockhash ?? (await connection.getLatestBlockhash("confirmed")).blockhash;
48253
- const message = new web3_js.TransactionMessage({
48254
- payerKey: authority,
48255
- recentBlockhash: blockhash,
48256
- instructions: mergeIx
48257
- }).compileToV0Message(luts);
48258
- const tx = new web3_js.VersionedTransaction(message);
48259
- return addTransactionMetadata(tx, {
48260
- addressLookupTables: luts,
48261
- type: "MERGE_STAKE_ACCOUNTS" /* MERGE_STAKE_ACCOUNTS */
48502
+ sizeConstraint: swapConstraints.sizeConstraint,
48503
+ swapIxCount: swapResponses.swapInstructions.length,
48504
+ swapLutCount: swapResponses.addressLookupTableAddresses.length
48505
+ });
48506
+ const flashloanTx = await makeFlashLoanTx({
48507
+ program,
48508
+ marginfiAccount,
48509
+ bankMap,
48510
+ addressLookupTableAccounts: luts,
48511
+ blockhash,
48512
+ ixs: allNonFlIxs,
48513
+ isSync: true
48262
48514
  });
48515
+ const txSize = getTxSize(flashloanTx);
48516
+ const totalKeys = getTotalAccountKeys(flashloanTx);
48517
+ if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
48518
+ throw TransactionBuildingError.swapSizeExceededPositionSwap(
48519
+ txSize,
48520
+ totalKeys,
48521
+ swapOpts.swapConfig?.provider
48522
+ );
48523
+ }
48524
+ return {
48525
+ flashloanTx,
48526
+ setupInstructions: swapResponses.setupInstructions,
48527
+ swapQuote: quoteResponse,
48528
+ borrowIxs,
48529
+ repayIxs
48530
+ };
48263
48531
  }
48264
48532
 
48265
48533
  // src/services/account/services/account-simulation.service.ts
@@ -48885,17 +49153,528 @@ function computeMaxWithdrawForBank(params) {
48885
49153
  const maxWithdraw = initUntiedCollateralForBank.div(initWeightedPrice);
48886
49154
  return maxWithdraw;
48887
49155
  }
49156
+ var TITAN_FEE_WALLET = new web3_js.PublicKey("6ryqDDCwKFZfSiHQrYRkjTEarbsLjg9TmuFg1RJorBk3");
49157
+ var getTitanFeeAccount = (mint) => {
49158
+ return getAssociatedTokenAddressSync(mint, TITAN_FEE_WALLET, true);
49159
+ };
49160
+ var checkTitanFeeAccount = async (connection, mint) => {
49161
+ const feeAccount = getTitanFeeAccount(mint);
49162
+ const hasFeeAccount = !!await connection.getAccountInfo(feeAccount);
49163
+ return { feeAccount, hasFeeAccount, feeWallet: TITAN_FEE_WALLET };
49164
+ };
49165
+ function deserializeTitanInstruction(ix) {
49166
+ return new web3_js.TransactionInstruction({
49167
+ programId: new web3_js.PublicKey(ix.p),
49168
+ keys: ix.a.map((account) => ({
49169
+ pubkey: new web3_js.PublicKey(account.p),
49170
+ isSigner: account.s,
49171
+ isWritable: account.w
49172
+ })),
49173
+ data: Buffer.from(ix.d)
49174
+ });
49175
+ }
49176
+ var getTitanSwapIxsForFlashloan = async ({
49177
+ quoteParams,
49178
+ authority,
49179
+ connection,
49180
+ destinationTokenAccount,
49181
+ apiConfig
49182
+ }) => {
49183
+ const basePath = apiConfig?.basePath ?? "";
49184
+ const feeMint = new web3_js.PublicKey(
49185
+ quoteParams.swapMode === "ExactIn" ? quoteParams.outputMint : quoteParams.inputMint
49186
+ );
49187
+ const { feeAccount, hasFeeAccount } = await checkTitanFeeAccount(connection, feeMint);
49188
+ let finalQuoteParams = quoteParams;
49189
+ if (!hasFeeAccount) {
49190
+ console.warn("Warning: Titan fee account ATA does not exist, disabling platform fee");
49191
+ finalQuoteParams = {
49192
+ ...quoteParams,
49193
+ platformFeeBps: void 0
49194
+ };
49195
+ }
49196
+ if (basePath.startsWith("wss://") || basePath.startsWith("ws://")) {
49197
+ return getTitanSwapIxsViaWebSocket(
49198
+ { quoteParams: finalQuoteParams, authority, connection, destinationTokenAccount, apiConfig },
49199
+ hasFeeAccount ? feeAccount : void 0
49200
+ );
49201
+ } else {
49202
+ return getTitanSwapIxsViaHttpProxy(
49203
+ { quoteParams: finalQuoteParams, authority, connection, destinationTokenAccount, apiConfig },
49204
+ hasFeeAccount ? feeAccount : void 0
49205
+ );
49206
+ }
49207
+ };
49208
+ async function getTitanSwapIxsViaWebSocket(params, feeAccount) {
49209
+ const { quoteParams, authority, connection, destinationTokenAccount, apiConfig } = params;
49210
+ const {
49211
+ inputMint,
49212
+ outputMint,
49213
+ amount,
49214
+ swapMode,
49215
+ slippageBps,
49216
+ platformFeeBps,
49217
+ directRoutesOnly,
49218
+ sizeConstraint,
49219
+ maxSwapAccounts,
49220
+ maxSwapTotalAccounts
49221
+ } = quoteParams;
49222
+ const wsUrl = apiConfig?.basePath;
49223
+ if (!wsUrl) {
49224
+ throw new Error("Titan WebSocket URL is required (apiConfig.basePath)");
49225
+ }
49226
+ const txParams = {
49227
+ userPublicKey: authority.toBytes(),
49228
+ outputAccount: destinationTokenAccount.toBytes()
49229
+ };
49230
+ if (feeAccount && platformFeeBps) {
49231
+ txParams.feeBps = platformFeeBps;
49232
+ txParams.feeAccount = feeAccount.toBytes();
49233
+ }
49234
+ const client = await V1Client.connect(wsUrl);
49235
+ try {
49236
+ const { stream } = await client.newSwapQuoteStream({
49237
+ swap: {
49238
+ inputMint: new web3_js.PublicKey(inputMint).toBytes(),
49239
+ outputMint: new web3_js.PublicKey(outputMint).toBytes(),
49240
+ amount,
49241
+ swapMode,
49242
+ slippageBps,
49243
+ onlyDirectRoutes: directRoutesOnly,
49244
+ addSizeConstraint: sizeConstraint !== void 0,
49245
+ sizeConstraint,
49246
+ accountsLimitWritable: maxSwapAccounts,
49247
+ accountsLimitTotal: maxSwapTotalAccounts
49248
+ },
49249
+ transaction: txParams,
49250
+ update: {
49251
+ num_quotes: 3
49252
+ }
49253
+ });
49254
+ const reader = stream.getReader();
49255
+ const { value: swapQuotes, done } = await reader.read();
49256
+ reader.releaseLock();
49257
+ if (done || !swapQuotes) {
49258
+ throw new Error("Titan swap quote stream ended without data");
49259
+ }
49260
+ const quotes = swapQuotes;
49261
+ const bestRoute = selectBestRoute(quotes.quotes, swapMode);
49262
+ if (!bestRoute) {
49263
+ throw new Error(`No Titan swap routes found for ${inputMint} -> ${outputMint}`);
49264
+ }
49265
+ const swapInstructions = bestRoute.instructions.map(deserializeTitanInstruction);
49266
+ const lutPubkeys = bestRoute.addressLookupTables.map((lutBytes) => new web3_js.PublicKey(lutBytes));
49267
+ const addressLookupTableAddresses = await resolveLookupTables(connection, lutPubkeys);
49268
+ const quoteResponse = {
49269
+ ...buildSwapQuoteResult(bestRoute, swapMode),
49270
+ provider: "TITAN" /* TITAN */
49271
+ };
49272
+ return {
49273
+ swapInstructions,
49274
+ setupInstructions: [],
49275
+ addressLookupTableAddresses,
49276
+ quoteResponse
49277
+ };
49278
+ } finally {
49279
+ if (!client.closed) {
49280
+ await client.close();
49281
+ }
49282
+ }
49283
+ }
49284
+ async function getTitanSwapIxsViaHttpProxy(params, feeAccount) {
49285
+ const { quoteParams, authority, connection, destinationTokenAccount, apiConfig } = params;
49286
+ const {
49287
+ inputMint,
49288
+ outputMint,
49289
+ amount,
49290
+ swapMode,
49291
+ slippageBps,
49292
+ platformFeeBps,
49293
+ directRoutesOnly,
49294
+ sizeConstraint,
49295
+ maxSwapAccounts,
49296
+ maxSwapTotalAccounts
49297
+ } = quoteParams;
49298
+ const basePath = apiConfig?.basePath;
49299
+ if (!basePath) {
49300
+ throw new Error("Titan proxy URL is required (apiConfig.basePath)");
49301
+ }
49302
+ const txBody = {
49303
+ userPublicKey: authority.toBase58(),
49304
+ outputAccount: destinationTokenAccount.toBase58()
49305
+ };
49306
+ if (feeAccount && platformFeeBps) {
49307
+ txBody.feeBps = platformFeeBps;
49308
+ txBody.feeAccount = feeAccount.toBase58();
49309
+ }
49310
+ const response = await fetch(`${basePath}/swap-quote`, {
49311
+ method: "POST",
49312
+ headers: {
49313
+ "Content-Type": "application/json",
49314
+ ...apiConfig?.headers ?? {}
49315
+ },
49316
+ body: JSON.stringify({
49317
+ swap: {
49318
+ inputMint,
49319
+ outputMint,
49320
+ amount,
49321
+ swapMode,
49322
+ slippageBps,
49323
+ onlyDirectRoutes: directRoutesOnly,
49324
+ addSizeConstraint: sizeConstraint !== void 0,
49325
+ sizeConstraint,
49326
+ accountsLimitWritable: maxSwapAccounts,
49327
+ accountsLimitTotal: maxSwapTotalAccounts
49328
+ },
49329
+ transaction: txBody
49330
+ })
49331
+ });
49332
+ if (!response.ok) {
49333
+ const errorData = await response.json().catch(() => ({}));
49334
+ throw new Error(
49335
+ `Titan proxy error (${response.status}): ${errorData.message ?? response.statusText}`
49336
+ );
49337
+ }
49338
+ const data = await response.json();
49339
+ const bestRoute = selectBestRoute(data.quotes, swapMode);
49340
+ if (!bestRoute) {
49341
+ throw new Error(`No Titan swap routes found for ${inputMint} -> ${outputMint}`);
49342
+ }
49343
+ const swapInstructions = bestRoute.instructions.map(deserializeSerializedInstruction);
49344
+ const lutPubkeys = bestRoute.addressLookupTables.map(
49345
+ (b64) => new web3_js.PublicKey(Buffer.from(b64, "base64"))
49346
+ );
49347
+ const addressLookupTableAddresses = await resolveLookupTables(connection, lutPubkeys);
49348
+ const quoteResponse = {
49349
+ ...buildSwapQuoteResult(bestRoute, swapMode),
49350
+ provider: "TITAN" /* TITAN */
49351
+ };
49352
+ return {
49353
+ swapInstructions,
49354
+ setupInstructions: [],
49355
+ addressLookupTableAddresses,
49356
+ quoteResponse
49357
+ };
49358
+ }
49359
+ var getTitanExactOutEstimate = async (params) => {
49360
+ const basePath = params.apiConfig?.basePath ?? "";
49361
+ if (basePath.startsWith("wss://") || basePath.startsWith("ws://")) {
49362
+ return getTitanExactOutViaWebSocket(params);
49363
+ } else {
49364
+ return getTitanExactOutViaHttpProxy(params);
49365
+ }
49366
+ };
49367
+ async function getTitanExactOutViaWebSocket(params) {
49368
+ const { inputMint, outputMint, amount, slippageBps, apiConfig } = params;
49369
+ const wsUrl = apiConfig?.basePath;
49370
+ if (!wsUrl) {
49371
+ throw new Error("Titan WebSocket URL is required (apiConfig.basePath)");
49372
+ }
49373
+ const client = await V1Client.connect(wsUrl);
49374
+ try {
49375
+ const { stream } = await client.newSwapQuoteStream({
49376
+ swap: {
49377
+ inputMint: new web3_js.PublicKey(inputMint).toBytes(),
49378
+ outputMint: new web3_js.PublicKey(outputMint).toBytes(),
49379
+ amount,
49380
+ swapMode: "ExactOut" /* ExactOut */,
49381
+ slippageBps
49382
+ },
49383
+ transaction: {
49384
+ userPublicKey: new Uint8Array(32)
49385
+ // placeholder, not executing
49386
+ },
49387
+ update: {
49388
+ num_quotes: 1
49389
+ }
49390
+ });
49391
+ const reader = stream.getReader();
49392
+ const { value: swapQuotes, done } = await reader.read();
49393
+ reader.releaseLock();
49394
+ if (done || !swapQuotes) {
49395
+ throw new Error("Titan ExactOut estimate stream ended without data");
49396
+ }
49397
+ const quotes = swapQuotes;
49398
+ const bestRoute = selectBestRoute(quotes.quotes, "ExactOut");
49399
+ if (!bestRoute) {
49400
+ throw new Error(`No Titan ExactOut routes found for ${inputMint} -> ${outputMint}`);
49401
+ }
49402
+ const quoteResult = {
49403
+ ...buildSwapQuoteResult(bestRoute, "ExactOut"),
49404
+ provider: "TITAN" /* TITAN */
49405
+ };
49406
+ return {
49407
+ otherAmountThreshold: quoteResult.otherAmountThreshold,
49408
+ quoteResult
49409
+ };
49410
+ } finally {
49411
+ if (!client.closed) {
49412
+ await client.close();
49413
+ }
49414
+ }
49415
+ }
49416
+ async function getTitanExactOutViaHttpProxy(params) {
49417
+ const { inputMint, outputMint, amount, slippageBps, apiConfig } = params;
49418
+ const basePath = apiConfig?.basePath;
49419
+ if (!basePath) {
49420
+ throw new Error("Titan proxy URL is required (apiConfig.basePath)");
49421
+ }
49422
+ const response = await fetch(`${basePath}/exact-out-estimate`, {
49423
+ method: "POST",
49424
+ headers: {
49425
+ "Content-Type": "application/json",
49426
+ ...apiConfig?.headers ?? {}
49427
+ },
49428
+ body: JSON.stringify({
49429
+ inputMint,
49430
+ outputMint,
49431
+ amount,
49432
+ slippageBps
49433
+ })
49434
+ });
49435
+ if (!response.ok) {
49436
+ const errorData = await response.json().catch(() => ({}));
49437
+ throw new Error(
49438
+ `Titan proxy error (${response.status}): ${errorData.message ?? response.statusText}`
49439
+ );
49440
+ }
49441
+ const data = await response.json();
49442
+ const quoteResult = {
49443
+ inAmount: String(data.inAmount),
49444
+ outAmount: String(data.outAmount),
49445
+ otherAmountThreshold: data.otherAmountThreshold,
49446
+ slippageBps: data.slippageBps,
49447
+ provider: "TITAN" /* TITAN */
49448
+ };
49449
+ return {
49450
+ otherAmountThreshold: data.otherAmountThreshold,
49451
+ quoteResult
49452
+ };
49453
+ }
49454
+
49455
+ // src/services/account/utils/swap.utils.ts
49456
+ function getSwapProviderFn({
49457
+ attemptProvider,
49458
+ maxSwapTotalAccounts,
49459
+ inputMint,
49460
+ outputMint,
49461
+ amount,
49462
+ swapMode,
49463
+ authority,
49464
+ connection,
49465
+ destinationTokenAccount,
49466
+ swapOpts,
49467
+ sizeConstraint
49468
+ }) {
49469
+ switch (attemptProvider) {
49470
+ case "TITAN" /* TITAN */:
49471
+ return (apiConfig) => getTitanSwapIxsForFlashloan({
49472
+ quoteParams: {
49473
+ inputMint,
49474
+ outputMint,
49475
+ amount,
49476
+ swapMode,
49477
+ slippageBps: swapOpts.swapConfig?.slippageBps,
49478
+ platformFeeBps: swapOpts.swapConfig?.platformFeeBps,
49479
+ directRoutesOnly: swapOpts.swapConfig?.directRoutesOnly,
49480
+ sizeConstraint,
49481
+ maxSwapTotalAccounts
49482
+ },
49483
+ authority,
49484
+ connection,
49485
+ destinationTokenAccount,
49486
+ apiConfig
49487
+ });
49488
+ case "JUPITER" /* JUPITER */:
49489
+ return (apiConfig) => getJupiterSwapIxsForFlashloan({
49490
+ quoteParams: {
49491
+ inputMint,
49492
+ outputMint,
49493
+ amount,
49494
+ swapMode,
49495
+ dynamicSlippage: swapOpts.swapConfig ? swapOpts.swapConfig.slippageMode === "DYNAMIC" : true,
49496
+ slippageBps: swapOpts.swapConfig?.slippageBps,
49497
+ platformFeeBps: swapOpts.swapConfig?.platformFeeBps,
49498
+ onlyDirectRoutes: swapOpts.swapConfig?.directRoutesOnly ?? false
49499
+ },
49500
+ authority,
49501
+ connection,
49502
+ destinationTokenAccount,
49503
+ apiConfig,
49504
+ maxSwapAccounts: maxSwapTotalAccounts
49505
+ });
49506
+ default:
49507
+ return void 0;
49508
+ }
49509
+ }
49510
+ function getExactOutProviderFn({
49511
+ attemptProvider,
49512
+ inputMint,
49513
+ outputMint,
49514
+ amount,
49515
+ swapOpts,
49516
+ apiConfig
49517
+ }) {
49518
+ switch (attemptProvider) {
49519
+ case "TITAN" /* TITAN */:
49520
+ return () => getTitanExactOutEstimate({
49521
+ inputMint,
49522
+ outputMint,
49523
+ amount,
49524
+ slippageBps: swapOpts.swapConfig?.slippageBps,
49525
+ apiConfig
49526
+ });
49527
+ case "JUPITER" /* JUPITER */:
49528
+ return async () => {
49529
+ const configParams = toJupiterConfig(apiConfig);
49530
+ const jupiterApiClient = configParams?.basePath ? new api.SwapApi(new api.Configuration(configParams)) : api.createJupiterApiClient(configParams);
49531
+ const estimateQuote = await jupiterApiClient.quoteGet({
49532
+ inputMint,
49533
+ outputMint,
49534
+ amount,
49535
+ swapMode: "ExactOut",
49536
+ dynamicSlippage: swapOpts.swapConfig ? swapOpts.swapConfig.slippageMode === "DYNAMIC" : true,
49537
+ slippageBps: swapOpts.swapConfig?.slippageBps
49538
+ });
49539
+ const quoteResult = mapJupiterQuoteToSwapQuoteResult(estimateQuote);
49540
+ return { otherAmountThreshold: quoteResult.otherAmountThreshold, quoteResult };
49541
+ };
49542
+ default:
49543
+ return void 0;
49544
+ }
49545
+ }
49546
+ var getSwapIxsForFlashloan = async (params) => {
49547
+ const {
49548
+ inputMint,
49549
+ outputMint,
49550
+ amount,
49551
+ swapMode,
49552
+ authority,
49553
+ connection,
49554
+ destinationTokenAccount,
49555
+ swapOpts,
49556
+ sizeConstraint,
49557
+ maxSwapTotalAccounts
49558
+ } = params;
49559
+ if (swapOpts.swapIxs) {
49560
+ return {
49561
+ swapInstructions: swapOpts.swapIxs.instructions,
49562
+ setupInstructions: [],
49563
+ addressLookupTableAddresses: swapOpts.swapIxs.lookupTables,
49564
+ quoteResponse: {
49565
+ inAmount: String(amount),
49566
+ outAmount: "0",
49567
+ otherAmountThreshold: "0",
49568
+ slippageBps: 0
49569
+ }
49570
+ };
49571
+ }
49572
+ const provider = swapOpts.swapConfig?.provider ?? "JUPITER" /* JUPITER */;
49573
+ const attempts = [
49574
+ { provider, apiConfig: swapOpts.swapConfig?.apiConfig },
49575
+ ...swapOpts.swapConfig?.fallbackProviders ?? []
49576
+ ];
49577
+ let lastError;
49578
+ for (const { provider: attemptProvider, apiConfig } of attempts) {
49579
+ const fn = getSwapProviderFn({
49580
+ attemptProvider,
49581
+ maxSwapTotalAccounts: params.maxSwapTotalAccounts,
49582
+ inputMint,
49583
+ outputMint,
49584
+ amount,
49585
+ swapMode,
49586
+ authority,
49587
+ connection,
49588
+ destinationTokenAccount,
49589
+ swapOpts,
49590
+ sizeConstraint
49591
+ });
49592
+ if (!fn) continue;
49593
+ try {
49594
+ return await fn(apiConfig);
49595
+ } catch (err) {
49596
+ if (err instanceof TransactionBuildingError) throw err;
49597
+ lastError = err;
49598
+ console.warn(`[swap] ${attemptProvider} failed:`, err instanceof Error ? err.message : err);
49599
+ }
49600
+ }
49601
+ const firstProvider = attempts[0]?.provider ?? "Swap";
49602
+ throw TransactionBuildingError.swapQuoteFailed(
49603
+ firstProvider,
49604
+ inputMint,
49605
+ outputMint,
49606
+ lastError?.message ?? "No swap route available"
49607
+ );
49608
+ };
49609
+ var getExactOutEstimate = async (params) => {
49610
+ const { inputMint, outputMint, amount, swapOpts, connection } = params;
49611
+ const provider = swapOpts.swapConfig?.provider ?? "JUPITER" /* JUPITER */;
49612
+ const attempts = [
49613
+ { provider, apiConfig: swapOpts.swapConfig?.apiConfig },
49614
+ ...swapOpts.swapConfig?.fallbackProviders ?? []
49615
+ ];
49616
+ let lastError;
49617
+ for (const { provider: attemptProvider, apiConfig } of attempts) {
49618
+ const fn = getExactOutProviderFn({
49619
+ attemptProvider,
49620
+ inputMint,
49621
+ outputMint,
49622
+ amount,
49623
+ swapOpts,
49624
+ apiConfig
49625
+ });
49626
+ if (!fn) continue;
49627
+ try {
49628
+ return await fn(apiConfig);
49629
+ } catch (err) {
49630
+ if (err instanceof TransactionBuildingError) throw err;
49631
+ lastError = err;
49632
+ console.warn(
49633
+ `[exactout] ${attemptProvider} failed:`,
49634
+ err instanceof Error ? err.message : err
49635
+ );
49636
+ }
49637
+ }
49638
+ const firstProvider = attempts[0]?.provider ?? "Swap";
49639
+ throw TransactionBuildingError.swapQuoteFailed(
49640
+ firstProvider,
49641
+ inputMint,
49642
+ outputMint,
49643
+ lastError?.message ?? "No swap route available"
49644
+ );
49645
+ };
49646
+ function mapJupiterQuoteToSwapQuoteResult(quote) {
49647
+ return {
49648
+ inAmount: quote.inAmount,
49649
+ outAmount: quote.outAmount,
49650
+ otherAmountThreshold: quote.otherAmountThreshold,
49651
+ slippageBps: quote.slippageBps,
49652
+ platformFee: quote.platformFee ? {
49653
+ amount: quote.platformFee.amount ?? "0",
49654
+ feeBps: quote.platformFee.feeBps ?? 0
49655
+ } : void 0,
49656
+ priceImpactPct: quote.priceImpactPct,
49657
+ contextSlot: quote.contextSlot,
49658
+ timeTaken: quote.timeTaken,
49659
+ provider: "JUPITER" /* JUPITER */
49660
+ };
49661
+ }
49662
+
49663
+ // src/services/account/utils/jupiter.utils.ts
48888
49664
  var REFERRAL_PROGRAM_ID = new web3_js.PublicKey("REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3");
48889
49665
  var REFERRAL_ACCOUNT_PUBKEY = new web3_js.PublicKey("Mm7HcujSK2JzPW4eX7g4oqTXbWYDuFxapNMHXe8yp1B");
48890
49666
  var getFeeAccount = (mint) => {
48891
- const referralProgramPubkey = REFERRAL_PROGRAM_ID;
48892
- const referralAccountPubkey = REFERRAL_ACCOUNT_PUBKEY;
48893
49667
  const [feeAccount] = web3_js.PublicKey.findProgramAddressSync(
48894
- [Buffer.from("referral_ata"), referralAccountPubkey.toBuffer(), mint.toBuffer()],
48895
- referralProgramPubkey
49668
+ [Buffer.from("referral_ata"), REFERRAL_ACCOUNT_PUBKEY.toBuffer(), mint.toBuffer()],
49669
+ REFERRAL_PROGRAM_ID
48896
49670
  );
48897
49671
  return feeAccount.toBase58();
48898
49672
  };
49673
+ var checkFeeAccount = async (connection, mint) => {
49674
+ const feeAccount = getFeeAccount(mint);
49675
+ const hasFeeAccount = !!await connection.getAccountInfo(new web3_js.PublicKey(feeAccount));
49676
+ return { feeAccount, hasFeeAccount };
49677
+ };
48899
49678
  function deserializeJupiterInstruction(instruction) {
48900
49679
  return new web3_js.TransactionInstruction({
48901
49680
  programId: new web3_js.PublicKey(instruction.programId),
@@ -48907,17 +49686,26 @@ function deserializeJupiterInstruction(instruction) {
48907
49686
  data: Buffer.from(instruction.data, "base64")
48908
49687
  });
48909
49688
  }
49689
+ function toJupiterConfig(apiConfig) {
49690
+ if (!apiConfig) return void 0;
49691
+ return {
49692
+ basePath: apiConfig.basePath,
49693
+ apiKey: apiConfig.apiKey ? () => apiConfig.apiKey : void 0,
49694
+ headers: apiConfig.headers
49695
+ };
49696
+ }
48910
49697
  var getJupiterSwapIxsForFlashloan = async ({
48911
49698
  quoteParams,
48912
49699
  authority,
48913
49700
  connection,
48914
49701
  destinationTokenAccount,
48915
- configParams
49702
+ apiConfig,
49703
+ maxSwapAccounts
48916
49704
  }) => {
49705
+ const configParams = toJupiterConfig(apiConfig);
48917
49706
  const jupiterApiClient = configParams?.basePath ? new api.SwapApi(new api.Configuration(configParams)) : api.createJupiterApiClient(configParams);
48918
49707
  const feeMint = quoteParams.swapMode === "ExactIn" ? quoteParams.outputMint : quoteParams.inputMint;
48919
- const feeAccount = getFeeAccount(new web3_js.PublicKey(feeMint));
48920
- const hasFeeAccount = !!await connection.getAccountInfo(new web3_js.PublicKey(feeAccount));
49708
+ const { feeAccount, hasFeeAccount } = await checkFeeAccount(connection, new web3_js.PublicKey(feeMint));
48921
49709
  const project0JupiterLut = (await connection.getAddressLookupTable(ADDRESS_LOOKUP_TABLE_FOR_SWAP))?.value;
48922
49710
  let finalQuoteParams = quoteParams;
48923
49711
  if (!hasFeeAccount) {
@@ -48927,67 +49715,45 @@ var getJupiterSwapIxsForFlashloan = async ({
48927
49715
  platformFeeBps: void 0
48928
49716
  };
48929
49717
  }
48930
- const maxAccountsArr = [40, 30];
48931
- const swapQuotes = await Promise.all(
48932
- maxAccountsArr.map((maxAccounts) => {
48933
- return jupiterApiClient.quoteGet({
48934
- ...finalQuoteParams,
48935
- maxAccounts
48936
- });
48937
- })
48938
- );
48939
- hasFeeAccount && finalQuoteParams.platformFeeBps && finalQuoteParams.platformFeeBps > 0;
48940
- const swapInstructionResponses = await Promise.all(
48941
- swapQuotes.map(
48942
- (quote) => jupiterApiClient.swapInstructionsPost({
48943
- swapRequest: {
48944
- quoteResponse: quote,
48945
- userPublicKey: authority.toBase58(),
48946
- feeAccount: hasFeeAccount ? feeAccount : void 0,
48947
- wrapAndUnwrapSol: false,
48948
- destinationTokenAccount: destinationTokenAccount.toBase58()
48949
- }
48950
- })
48951
- )
48952
- );
48953
- const lutAddresses = swapInstructionResponses.map(
48954
- (swapInstructionResponse) => swapInstructionResponse.addressLookupTableAddresses
48955
- );
49718
+ const JUPITER_MAX_ACCOUNTS_MARGIN = 4;
49719
+ const maxAccounts = maxSwapAccounts !== void 0 ? maxSwapAccounts - JUPITER_MAX_ACCOUNTS_MARGIN : 40;
49720
+ const swapQuote = await jupiterApiClient.quoteGet({
49721
+ ...finalQuoteParams,
49722
+ maxAccounts
49723
+ });
49724
+ const swapInstructionResponse = await jupiterApiClient.swapInstructionsPost({
49725
+ swapRequest: {
49726
+ quoteResponse: swapQuote,
49727
+ userPublicKey: authority.toBase58(),
49728
+ feeAccount: hasFeeAccount ? feeAccount : void 0,
49729
+ wrapAndUnwrapSol: false,
49730
+ destinationTokenAccount: destinationTokenAccount.toBase58()
49731
+ }
49732
+ });
49733
+ const lutAddresses = swapInstructionResponse.addressLookupTableAddresses;
48956
49734
  const lutAccountsRaw = await connection.getMultipleAccountsInfo(
48957
- lutAddresses.flat().map((address) => new web3_js.PublicKey(address))
49735
+ lutAddresses.map((address) => new web3_js.PublicKey(address))
48958
49736
  );
48959
- let currentIndex = 0;
48960
- const jupiterSwapIxs = [];
48961
- for (let i = 0; i < swapInstructionResponses.length; i++) {
48962
- const response = swapInstructionResponses[i];
48963
- const quote = swapQuotes[i];
48964
- if (!response || !quote) continue;
48965
- const address = response.addressLookupTableAddresses;
48966
- const addressesLength = address.length;
48967
- const addressesStartIndex = currentIndex;
48968
- const addressesEndIndex = addressesStartIndex + addressesLength;
48969
- currentIndex = addressesEndIndex;
48970
- const lutAccounts = lutAccountsRaw.slice(addressesStartIndex, addressesEndIndex);
48971
- const addressLookupTableAccounts = lutAccounts.map((accountInfo, index) => {
48972
- const addressLookupTableAddress = address[index];
48973
- if (!accountInfo || !addressLookupTableAddress) {
48974
- return null;
48975
- }
48976
- return new web3_js.AddressLookupTableAccount({
48977
- key: new web3_js.PublicKey(addressLookupTableAddress),
48978
- state: web3_js.AddressLookupTableAccount.deserialize(accountInfo.data)
48979
- });
48980
- }).filter((account) => account !== null).concat(project0JupiterLut ? [project0JupiterLut] : []);
48981
- const instruction = deserializeJupiterInstruction(response.swapInstruction);
48982
- const setupInstructions = response.setupInstructions.map(deserializeJupiterInstruction);
48983
- jupiterSwapIxs.push({
48984
- swapInstruction: instruction,
48985
- setupInstructions,
48986
- addressLookupTableAddresses: addressLookupTableAccounts,
48987
- quoteResponse: quote
49737
+ const addressLookupTableAccounts = lutAccountsRaw.map((accountInfo, index) => {
49738
+ const addressLookupTableAddress = lutAddresses[index];
49739
+ if (!accountInfo || !addressLookupTableAddress) {
49740
+ return null;
49741
+ }
49742
+ return new web3_js.AddressLookupTableAccount({
49743
+ key: new web3_js.PublicKey(addressLookupTableAddress),
49744
+ state: web3_js.AddressLookupTableAccount.deserialize(accountInfo.data)
48988
49745
  });
48989
- }
48990
- return jupiterSwapIxs;
49746
+ }).filter((account) => account !== null).concat(project0JupiterLut ? [project0JupiterLut] : []);
49747
+ const instruction = deserializeJupiterInstruction(swapInstructionResponse.swapInstruction);
49748
+ const setupInstructions = swapInstructionResponse.setupInstructions.map(
49749
+ deserializeJupiterInstruction
49750
+ );
49751
+ return {
49752
+ swapInstructions: [instruction],
49753
+ setupInstructions,
49754
+ addressLookupTableAddresses: addressLookupTableAccounts,
49755
+ quoteResponse: mapJupiterQuoteToSwapQuoteResult(swapQuote)
49756
+ };
48991
49757
  };
48992
49758
 
48993
49759
  // src/services/account/utils/misc.utils.ts
@@ -49005,6 +49771,440 @@ function isWholePosition(position, amount, mintDecimals) {
49005
49771
  const closePositionTokenAmount = computeClosePositionTokenAmount(position, mintDecimals);
49006
49772
  return amount >= closePositionTokenAmount;
49007
49773
  }
49774
+ var SWAP_MERGE_OVERHEAD = 150;
49775
+ var FL_IX_OVERHEAD = 52;
49776
+ function compactU16Size(n) {
49777
+ return n < 128 ? 1 : n < 16384 ? 2 : 3;
49778
+ }
49779
+ function computeV0TxSize(ixs, payerKey, luts) {
49780
+ const keyMap = /* @__PURE__ */ new Map();
49781
+ const payerStr = payerKey.toBase58();
49782
+ keyMap.set(payerStr, { isSigner: true, isWritable: true });
49783
+ const programIds = /* @__PURE__ */ new Set();
49784
+ for (const ix of ixs) {
49785
+ const progStr = ix.programId.toBase58();
49786
+ programIds.add(progStr);
49787
+ if (!keyMap.has(progStr)) {
49788
+ keyMap.set(progStr, { isSigner: false, isWritable: false });
49789
+ }
49790
+ for (const meta of ix.keys) {
49791
+ const keyStr = meta.pubkey.toBase58();
49792
+ const existing = keyMap.get(keyStr);
49793
+ if (existing) {
49794
+ existing.isSigner = existing.isSigner || meta.isSigner;
49795
+ existing.isWritable = existing.isWritable || meta.isWritable;
49796
+ } else {
49797
+ keyMap.set(keyStr, { isSigner: meta.isSigner, isWritable: meta.isWritable });
49798
+ }
49799
+ }
49800
+ }
49801
+ const lutLookup = /* @__PURE__ */ new Map();
49802
+ for (let li = 0; li < luts.length; li++) {
49803
+ const addresses = luts[li].state.addresses;
49804
+ for (let ai = 0; ai < addresses.length; ai++) {
49805
+ const addrStr = addresses[ai].toBase58();
49806
+ if (!lutLookup.has(addrStr)) {
49807
+ lutLookup.set(addrStr, { lutIdx: li, addrIdx: ai });
49808
+ }
49809
+ }
49810
+ }
49811
+ let numStaticKeys = 0;
49812
+ let numWritableStaticKeys = 0;
49813
+ const lutWritableIdxs = luts.map(() => /* @__PURE__ */ new Set());
49814
+ const lutReadonlyIdxs = luts.map(() => /* @__PURE__ */ new Set());
49815
+ for (const [keyStr, props] of keyMap) {
49816
+ if (props.isSigner || programIds.has(keyStr)) {
49817
+ numStaticKeys++;
49818
+ if (props.isWritable) numWritableStaticKeys++;
49819
+ continue;
49820
+ }
49821
+ const lutEntry = lutLookup.get(keyStr);
49822
+ if (lutEntry) {
49823
+ if (props.isWritable) {
49824
+ lutWritableIdxs[lutEntry.lutIdx].add(lutEntry.addrIdx);
49825
+ } else {
49826
+ lutReadonlyIdxs[lutEntry.lutIdx].add(lutEntry.addrIdx);
49827
+ }
49828
+ } else {
49829
+ numStaticKeys++;
49830
+ if (props.isWritable) numWritableStaticKeys++;
49831
+ }
49832
+ }
49833
+ const fixedOverhead = 101;
49834
+ const staticKeysSection = compactU16Size(numStaticKeys) + numStaticKeys * 32;
49835
+ let ixSection = compactU16Size(ixs.length);
49836
+ for (const ix of ixs) {
49837
+ const numAccounts = ix.keys.length;
49838
+ ixSection += 1 + // programId index
49839
+ compactU16Size(numAccounts) + numAccounts + // account key indexes
49840
+ compactU16Size(ix.data.length) + ix.data.length;
49841
+ }
49842
+ let numUsedLuts = 0;
49843
+ let lutSection = 0;
49844
+ for (let li = 0; li < luts.length; li++) {
49845
+ const wCount = lutWritableIdxs[li].size;
49846
+ const rCount = lutReadonlyIdxs[li].size;
49847
+ if (wCount === 0 && rCount === 0) continue;
49848
+ numUsedLuts++;
49849
+ lutSection += 32 + // LUT address
49850
+ compactU16Size(wCount) + wCount + // writable indexes
49851
+ compactU16Size(rCount) + rCount;
49852
+ }
49853
+ lutSection += compactU16Size(numUsedLuts);
49854
+ let totalLutKeys = 0;
49855
+ for (let li = 0; li < luts.length; li++) {
49856
+ totalLutKeys += lutWritableIdxs[li].size + lutReadonlyIdxs[li].size;
49857
+ }
49858
+ const accountCount = numStaticKeys + totalLutKeys;
49859
+ let totalLutWritableKeys = 0;
49860
+ for (let li = 0; li < luts.length; li++) {
49861
+ totalLutWritableKeys += lutWritableIdxs[li].size;
49862
+ }
49863
+ const writableAccountCount = numWritableStaticKeys + totalLutWritableKeys;
49864
+ const size = fixedOverhead + staticKeysSection + ixSection + lutSection + 1;
49865
+ return { size, accountCount, writableAccountCount };
49866
+ }
49867
+ function computeFlashLoanNonSwapBudget({
49868
+ program,
49869
+ marginfiAccount,
49870
+ ixs,
49871
+ bankMap,
49872
+ addressLookupTableAccounts
49873
+ }) {
49874
+ const projectedActiveBanksKeys = computeProjectedActiveBanksNoCpi(
49875
+ marginfiAccount.balances,
49876
+ ixs,
49877
+ program
49878
+ );
49879
+ const projectedActiveBanks = projectedActiveBanksKeys.map((key) => {
49880
+ const b = bankMap.get(key.toBase58());
49881
+ if (!b) throw new Error(`Bank ${key.toBase58()} not found in computeFlashLoanNonSwapBudget`);
49882
+ return b;
49883
+ });
49884
+ const endIndex = ixs.length + 1;
49885
+ const beginFlIx = sync_instructions_default.makeBeginFlashLoanIx(
49886
+ program.programId,
49887
+ { marginfiAccount: marginfiAccount.address, authority: marginfiAccount.authority },
49888
+ { endIndex: new BN11__default.default(endIndex) }
49889
+ );
49890
+ const endFlRemainingAccounts = computeHealthAccountMetas(projectedActiveBanks);
49891
+ const endFlIx = sync_instructions_default.makeEndFlashLoanIx(
49892
+ program.programId,
49893
+ { marginfiAccount: marginfiAccount.address, authority: marginfiAccount.authority },
49894
+ endFlRemainingAccounts.map((pubkey) => ({ pubkey, isSigner: false, isWritable: false }))
49895
+ );
49896
+ const allNonSwapIxs = [beginFlIx, ...ixs, endFlIx];
49897
+ const nonSwapMsg = new web3_js.TransactionMessage({
49898
+ payerKey: marginfiAccount.authority,
49899
+ recentBlockhash: web3_js.PublicKey.default.toBase58(),
49900
+ instructions: allNonSwapIxs
49901
+ }).compileToV0Message(addressLookupTableAccounts);
49902
+ const nonSwapSize = new web3_js.VersionedTransaction(nonSwapMsg).serialize().length;
49903
+ const { header, staticAccountKeys, addressTableLookups } = nonSwapMsg;
49904
+ const nonSwapTotal = staticAccountKeys.length + addressTableLookups.reduce(
49905
+ (s, l) => s + l.writableIndexes.length + l.readonlyIndexes.length,
49906
+ 0
49907
+ );
49908
+ const sizeConstraint = MAX_TX_SIZE - nonSwapSize - SWAP_MERGE_OVERHEAD;
49909
+ const maxSwapTotalAccounts = MAX_ACCOUNT_LOCKS - nonSwapTotal;
49910
+ console.log("[flashloan-budget]", {
49911
+ method: "compiled",
49912
+ nonSwapSize,
49913
+ nonSwapTotal,
49914
+ sizeConstraint,
49915
+ maxSwapTotalAccounts
49916
+ });
49917
+ return { sizeConstraint, maxSwapTotalAccounts };
49918
+ }
49919
+ function compileFlashloanPrecheck({
49920
+ allIxs,
49921
+ payer,
49922
+ luts,
49923
+ sizeConstraint,
49924
+ swapIxCount,
49925
+ swapLutCount
49926
+ }) {
49927
+ const msg = new web3_js.TransactionMessage({
49928
+ payerKey: payer,
49929
+ recentBlockhash: web3_js.PublicKey.default.toBase58(),
49930
+ instructions: allIxs
49931
+ }).compileToV0Message(luts);
49932
+ const rawSize = new web3_js.VersionedTransaction(msg).serialize().length;
49933
+ const fullTxSize = rawSize + FL_IX_OVERHEAD;
49934
+ const overshoot = fullTxSize - MAX_TX_SIZE;
49935
+ const { header, staticAccountKeys, addressTableLookups } = msg;
49936
+ const writableStatic = staticAccountKeys.length - header.numReadonlySignedAccounts - header.numReadonlyUnsignedAccounts;
49937
+ const writableLut = addressTableLookups.reduce((s, l) => s + l.writableIndexes.length, 0);
49938
+ const writableAccounts = writableStatic + writableLut;
49939
+ const totalAccounts = staticAccountKeys.length + addressTableLookups.reduce(
49940
+ (s, l) => s + l.writableIndexes.length + l.readonlyIndexes.length,
49941
+ 0
49942
+ );
49943
+ console.log("[flashloan-precheck]", {
49944
+ fullTxSize,
49945
+ overshoot,
49946
+ sizeConstraint,
49947
+ writableAccounts,
49948
+ totalAccounts,
49949
+ staticKeys: staticAccountKeys.length,
49950
+ numLuts: addressTableLookups.length,
49951
+ swapIxCount,
49952
+ swapLutCount
49953
+ });
49954
+ return { fullTxSize, overshoot, writableAccounts, totalAccounts };
49955
+ }
49956
+ async function buildBudgetIx(config, program, marginfiAccount, bankMap, bankMetadataMap, overrideInferAccounts) {
49957
+ const { bank, tokenProgram } = config;
49958
+ switch (config.type) {
49959
+ case "borrow":
49960
+ return makeBorrowIx3({
49961
+ program,
49962
+ bank,
49963
+ bankMap,
49964
+ tokenProgram,
49965
+ amount: 1,
49966
+ marginfiAccount,
49967
+ authority: marginfiAccount.authority,
49968
+ isSync: true,
49969
+ opts: { createAtas: false, wrapAndUnwrapSol: false, overrideInferAccounts }
49970
+ });
49971
+ case "repay":
49972
+ return makeRepayIx3({
49973
+ program,
49974
+ bank,
49975
+ tokenProgram,
49976
+ amount: 1,
49977
+ accountAddress: marginfiAccount.address,
49978
+ authority: marginfiAccount.authority,
49979
+ repayAll: false,
49980
+ isSync: true,
49981
+ opts: { wrapAndUnwrapSol: false, overrideInferAccounts }
49982
+ });
49983
+ case "deposit":
49984
+ return buildDepositBudgetIx(
49985
+ config,
49986
+ program,
49987
+ marginfiAccount,
49988
+ bankMetadataMap,
49989
+ overrideInferAccounts
49990
+ );
49991
+ case "withdraw":
49992
+ return buildWithdrawBudgetIx(
49993
+ config,
49994
+ program,
49995
+ marginfiAccount,
49996
+ bankMap,
49997
+ bankMetadataMap,
49998
+ overrideInferAccounts
49999
+ );
50000
+ }
50001
+ }
50002
+ async function buildDepositBudgetIx(config, program, marginfiAccount, bankMetadataMap, overrideInferAccounts) {
50003
+ const { bank, tokenProgram } = config;
50004
+ const opts = { wrapAndUnwrapSol: false, overrideInferAccounts };
50005
+ switch (bank.config.assetTag) {
50006
+ case 3 /* KAMINO */: {
50007
+ const reserve = bankMetadataMap[bank.address.toBase58()]?.kaminoStates?.reserveState;
50008
+ if (!reserve) {
50009
+ throw TransactionBuildingError.kaminoReserveNotFound(
50010
+ bank.address.toBase58(),
50011
+ bank.mint.toBase58(),
50012
+ bank.tokenSymbol
50013
+ );
50014
+ }
50015
+ return makeKaminoDepositIx3({
50016
+ program,
50017
+ bank,
50018
+ tokenProgram,
50019
+ amount: 1,
50020
+ accountAddress: marginfiAccount.address,
50021
+ authority: marginfiAccount.authority,
50022
+ group: marginfiAccount.group,
50023
+ reserve,
50024
+ isSync: true,
50025
+ opts
50026
+ });
50027
+ }
50028
+ case 4 /* DRIFT */: {
50029
+ const driftState = bankMetadataMap[bank.address.toBase58()]?.driftStates;
50030
+ if (!driftState) {
50031
+ throw TransactionBuildingError.driftStateNotFound(
50032
+ bank.address.toBase58(),
50033
+ bank.mint.toBase58(),
50034
+ bank.tokenSymbol
50035
+ );
50036
+ }
50037
+ return makeDriftDepositIx3({
50038
+ program,
50039
+ bank,
50040
+ tokenProgram,
50041
+ amount: 1,
50042
+ accountAddress: marginfiAccount.address,
50043
+ authority: marginfiAccount.authority,
50044
+ group: marginfiAccount.group,
50045
+ driftMarketIndex: driftState.spotMarketState.marketIndex,
50046
+ driftOracle: driftState.spotMarketState.oracle,
50047
+ isSync: true,
50048
+ opts
50049
+ });
50050
+ }
50051
+ case 6 /* JUPLEND */: {
50052
+ return makeJuplendDepositIx2({
50053
+ program,
50054
+ bank,
50055
+ tokenProgram,
50056
+ amount: 1,
50057
+ accountAddress: marginfiAccount.address,
50058
+ authority: marginfiAccount.authority,
50059
+ group: marginfiAccount.group,
50060
+ isSync: true,
50061
+ opts
50062
+ });
50063
+ }
50064
+ default: {
50065
+ return makeDepositIx3({
50066
+ program,
50067
+ bank,
50068
+ tokenProgram,
50069
+ amount: 1,
50070
+ accountAddress: marginfiAccount.address,
50071
+ authority: marginfiAccount.authority,
50072
+ group: marginfiAccount.group,
50073
+ isSync: true,
50074
+ opts
50075
+ });
50076
+ }
50077
+ }
50078
+ }
50079
+ async function buildWithdrawBudgetIx(config, program, marginfiAccount, bankMap, bankMetadataMap, overrideInferAccounts) {
50080
+ const { bank, tokenProgram } = config;
50081
+ const opts = { createAtas: false, wrapAndUnwrapSol: false, overrideInferAccounts };
50082
+ switch (bank.config.assetTag) {
50083
+ case 3 /* KAMINO */: {
50084
+ const reserve = bankMetadataMap[bank.address.toBase58()]?.kaminoStates?.reserveState;
50085
+ if (!reserve) {
50086
+ throw TransactionBuildingError.kaminoReserveNotFound(
50087
+ bank.address.toBase58(),
50088
+ bank.mint.toBase58(),
50089
+ bank.tokenSymbol
50090
+ );
50091
+ }
50092
+ return makeKaminoWithdrawIx3({
50093
+ program,
50094
+ bank,
50095
+ bankMap,
50096
+ tokenProgram,
50097
+ cTokenAmount: 1,
50098
+ marginfiAccount,
50099
+ authority: marginfiAccount.authority,
50100
+ reserve,
50101
+ withdrawAll: false,
50102
+ isSync: true,
50103
+ opts
50104
+ });
50105
+ }
50106
+ case 4 /* DRIFT */: {
50107
+ const driftState = bankMetadataMap[bank.address.toBase58()]?.driftStates;
50108
+ if (!driftState) {
50109
+ throw TransactionBuildingError.driftStateNotFound(
50110
+ bank.address.toBase58(),
50111
+ bank.mint.toBase58(),
50112
+ bank.tokenSymbol
50113
+ );
50114
+ }
50115
+ return makeDriftWithdrawIx3({
50116
+ program,
50117
+ bank,
50118
+ bankMap,
50119
+ tokenProgram,
50120
+ amount: 1,
50121
+ marginfiAccount,
50122
+ authority: marginfiAccount.authority,
50123
+ driftSpotMarket: driftState.spotMarketState,
50124
+ userRewards: driftState.userRewards,
50125
+ withdrawAll: false,
50126
+ isSync: true,
50127
+ opts
50128
+ });
50129
+ }
50130
+ case 6 /* JUPLEND */: {
50131
+ const jupLendState = bankMetadataMap[bank.address.toBase58()]?.jupLendStates;
50132
+ if (!jupLendState) {
50133
+ throw TransactionBuildingError.jupLendStateNotFound(
50134
+ bank.address.toBase58(),
50135
+ bank.mint.toBase58(),
50136
+ bank.tokenSymbol
50137
+ );
50138
+ }
50139
+ return makeJuplendWithdrawIx2({
50140
+ program,
50141
+ bank,
50142
+ bankMap,
50143
+ tokenProgram,
50144
+ amount: 1,
50145
+ marginfiAccount,
50146
+ authority: marginfiAccount.authority,
50147
+ jupLendingState: jupLendState.jupLendingState,
50148
+ withdrawAll: false,
50149
+ opts
50150
+ });
50151
+ }
50152
+ default: {
50153
+ return makeWithdrawIx3({
50154
+ program,
50155
+ bank,
50156
+ bankMap,
50157
+ tokenProgram,
50158
+ amount: 1,
50159
+ marginfiAccount,
50160
+ authority: marginfiAccount.authority,
50161
+ withdrawAll: false,
50162
+ isSync: true,
50163
+ opts
50164
+ });
50165
+ }
50166
+ }
50167
+ }
50168
+ async function computeFlashloanSwapConstraints({
50169
+ program,
50170
+ marginfiAccount,
50171
+ bankMap,
50172
+ addressLookupTableAccounts,
50173
+ bankMetadataMap,
50174
+ primaryIx,
50175
+ secondaryIx,
50176
+ overrideInferAccounts
50177
+ }) {
50178
+ const cuRequestIxs = [
50179
+ web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
50180
+ web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
50181
+ ];
50182
+ const [primaryResult, secondaryResult] = await Promise.all([
50183
+ buildBudgetIx(
50184
+ primaryIx,
50185
+ program,
50186
+ marginfiAccount,
50187
+ bankMap,
50188
+ bankMetadataMap,
50189
+ overrideInferAccounts
50190
+ ),
50191
+ buildBudgetIx(
50192
+ secondaryIx,
50193
+ program,
50194
+ marginfiAccount,
50195
+ bankMap,
50196
+ bankMetadataMap,
50197
+ overrideInferAccounts
50198
+ )
50199
+ ]);
50200
+ return computeFlashLoanNonSwapBudget({
50201
+ program,
50202
+ marginfiAccount,
50203
+ bankMap,
50204
+ addressLookupTableAccounts,
50205
+ ixs: [...cuRequestIxs, ...primaryResult.instructions, ...secondaryResult.instructions]
50206
+ });
50207
+ }
49008
50208
 
49009
50209
  // src/services/price/utils/smart-crank.utils.ts
49010
50210
  async function computeSmartCrank({
@@ -50208,7 +51408,7 @@ function computeMaxLeverage(depositBank, borrowBank, opts) {
50208
51408
  ltv
50209
51409
  };
50210
51410
  }
50211
- function computeLoopingParams(principal, targetLeverage, depositBank, borrowBank, depositOracleInfo, borrowOracleInfo, opts) {
51411
+ function computeLoopingParams(principal, targetLeverage, depositBank, borrowBank, depositPriceUsd, borrowPriceUsd, opts) {
50212
51412
  const initialCollateral = toBigNumber(principal);
50213
51413
  const { maxLeverage } = computeMaxLeverage(depositBank, borrowBank, opts);
50214
51414
  let clampedLeverage = targetLeverage;
@@ -50223,7 +51423,7 @@ function computeLoopingParams(principal, targetLeverage, depositBank, borrowBank
50223
51423
  }
50224
51424
  const totalDepositAmount = initialCollateral.times(new BigNumber3__default.default(clampedLeverage));
50225
51425
  const additionalDepositAmount = totalDepositAmount.minus(initialCollateral);
50226
- const totalBorrowAmount = additionalDepositAmount.times(depositOracleInfo.priceWeighted.lowestPrice).div(borrowOracleInfo.priceWeighted.highestPrice);
51426
+ const totalBorrowAmount = additionalDepositAmount.times(new BigNumber3__default.default(depositPriceUsd)).div(new BigNumber3__default.default(borrowPriceUsd));
50227
51427
  return {
50228
51428
  totalBorrowAmount: totalBorrowAmount.decimalPlaces(
50229
51429
  borrowBank.mintDecimals,
@@ -51267,6 +52467,230 @@ function getValidatorVoteAccountByBank() {
51267
52467
  }
51268
52468
  return _voteAccountByBank;
51269
52469
  }
52470
+ async function computeStakedBankMultipliers(stakedBanks, connection) {
52471
+ const multiplierByBank = /* @__PURE__ */ new Map();
52472
+ if (stakedBanks.length === 0) {
52473
+ return multiplierByBank;
52474
+ }
52475
+ const metadataMap = getStakedBankMetadataMap();
52476
+ const stakedBankAddresses = [];
52477
+ const poolStakeAddresses = [];
52478
+ const lstMintAddresses = [];
52479
+ for (const bank of stakedBanks) {
52480
+ const metadata = metadataMap.get(bank.address.toBase58());
52481
+ if (!metadata) {
52482
+ multiplierByBank.set(bank.address.toBase58(), new BigNumber3__default.default(1));
52483
+ continue;
52484
+ }
52485
+ const pool = findPoolAddress(new web3_js.PublicKey(metadata.validatorVoteAccount));
52486
+ stakedBankAddresses.push(bank.address.toBase58());
52487
+ poolStakeAddresses.push(findPoolStakeAddress(pool));
52488
+ lstMintAddresses.push(findPoolMintAddress(pool));
52489
+ }
52490
+ if (stakedBankAddresses.length === 0) {
52491
+ return multiplierByBank;
52492
+ }
52493
+ const allAddresses = [
52494
+ ...poolStakeAddresses.map((a) => a.toBase58()),
52495
+ ...lstMintAddresses.map((a) => a.toBase58())
52496
+ ];
52497
+ const accountInfos = await chunkedGetRawMultipleAccountInfoOrdered(connection, allAddresses);
52498
+ const poolStakeInfos = accountInfos.slice(0, poolStakeAddresses.length);
52499
+ const lstMintInfos = accountInfos.slice(poolStakeAddresses.length);
52500
+ for (let i = 0; i < stakedBankAddresses.length; i++) {
52501
+ const bankAddr = stakedBankAddresses[i];
52502
+ const poolStakeInfo = poolStakeInfos[i];
52503
+ const lstMintInfo = lstMintInfos[i];
52504
+ if (!poolStakeInfo || !lstMintInfo) {
52505
+ multiplierByBank.set(bankAddr, new BigNumber3__default.default(1));
52506
+ continue;
52507
+ }
52508
+ const stakeLamports = poolStakeInfo.lamports;
52509
+ const supplyBuffer = lstMintInfo.data.slice(36, 44);
52510
+ const lstMintSupply = Number(Buffer.from(supplyBuffer).readBigUInt64LE(0));
52511
+ if (lstMintSupply === 0) {
52512
+ multiplierByBank.set(bankAddr, new BigNumber3__default.default(1));
52513
+ continue;
52514
+ }
52515
+ const adjustedStake = Math.max(stakeLamports - web3_js.LAMPORTS_PER_SOL, 0);
52516
+ const multiplier = new BigNumber3__default.default(adjustedStake).dividedBy(lstMintSupply);
52517
+ multiplierByBank.set(bankAddr, multiplier);
52518
+ }
52519
+ return multiplierByBank;
52520
+ }
52521
+ var SYSVAR_CLOCK_ID2 = new web3_js.PublicKey("SysvarC1ock11111111111111111111111111111111");
52522
+ async function makeMintStakedLstIx(params) {
52523
+ const { amount, authority, stakeAccountPk, validator, connection } = params;
52524
+ const pool = findPoolAddress(validator);
52525
+ const lstMint = findPoolMintAddress(pool);
52526
+ const poolStakeAuth = findPoolStakeAuthorityAddress(pool);
52527
+ const lstAta = getAssociatedTokenAddressSync(lstMint, authority);
52528
+ const [lstAccInfo, stakeAccInfoParsed, rentExemptReserve] = await Promise.all([
52529
+ connection.getAccountInfo(lstAta),
52530
+ connection.getParsedAccountInfo(stakeAccountPk),
52531
+ connection.getMinimumBalanceForRentExemption(web3_js.StakeProgram.space)
52532
+ ]);
52533
+ const stakeAccParsed = stakeAccInfoParsed?.value?.data;
52534
+ const amountLamports = Math.round(Number(amount) * web3_js.LAMPORTS_PER_SOL);
52535
+ const stakeAccLamports = Number(stakeAccParsed?.parsed?.info?.stake?.delegation?.stake ?? 0);
52536
+ const isFullStake = amountLamports >= stakeAccLamports;
52537
+ const instructions2 = [];
52538
+ const signers = [];
52539
+ if (!lstAccInfo) {
52540
+ instructions2.push(
52541
+ createAssociatedTokenAccountInstruction(authority, lstAta, authority, lstMint)
52542
+ );
52543
+ }
52544
+ let targetStakePubkey;
52545
+ if (!isFullStake) {
52546
+ const splitStakeAccount = web3_js.Keypair.generate();
52547
+ signers.push(splitStakeAccount);
52548
+ targetStakePubkey = splitStakeAccount.publicKey;
52549
+ instructions2.push(
52550
+ ...web3_js.StakeProgram.split(
52551
+ {
52552
+ stakePubkey: stakeAccountPk,
52553
+ authorizedPubkey: authority,
52554
+ splitStakePubkey: splitStakeAccount.publicKey,
52555
+ lamports: amountLamports
52556
+ },
52557
+ rentExemptReserve
52558
+ ).instructions
52559
+ );
52560
+ } else {
52561
+ targetStakePubkey = stakeAccountPk;
52562
+ }
52563
+ const [authorizeStakerIx, authorizeWithdrawIx] = await Promise.all([
52564
+ web3_js.StakeProgram.authorize({
52565
+ stakePubkey: targetStakePubkey,
52566
+ authorizedPubkey: authority,
52567
+ newAuthorizedPubkey: poolStakeAuth,
52568
+ stakeAuthorizationType: web3_js.StakeAuthorizationLayout.Staker
52569
+ }).instructions,
52570
+ web3_js.StakeProgram.authorize({
52571
+ stakePubkey: targetStakePubkey,
52572
+ authorizedPubkey: authority,
52573
+ newAuthorizedPubkey: poolStakeAuth,
52574
+ stakeAuthorizationType: web3_js.StakeAuthorizationLayout.Withdrawer
52575
+ }).instructions
52576
+ ]);
52577
+ [authorizeStakerIx[0], authorizeWithdrawIx[0]].forEach((ix) => {
52578
+ if (ix) {
52579
+ ix.keys = ix.keys.map((key) => ({
52580
+ ...key,
52581
+ isWritable: key.pubkey.equals(SYSVAR_CLOCK_ID2) ? false : key.isWritable
52582
+ }));
52583
+ }
52584
+ });
52585
+ instructions2.push(...authorizeStakerIx, ...authorizeWithdrawIx);
52586
+ const depositStakeIx = await SinglePoolInstruction.depositStake(
52587
+ pool,
52588
+ targetStakePubkey,
52589
+ lstAta,
52590
+ authority
52591
+ );
52592
+ instructions2.push(depositStakeIx);
52593
+ return { instructions: instructions2, keys: signers };
52594
+ }
52595
+ async function makeMintStakedLstTx(params) {
52596
+ const { connection, luts, blockhash: providedBlockhash } = params;
52597
+ const { instructions: instructions2, keys } = await makeMintStakedLstIx(params);
52598
+ const blockhash = providedBlockhash ?? (await connection.getLatestBlockhash("confirmed")).blockhash;
52599
+ const message = new web3_js.TransactionMessage({
52600
+ payerKey: params.authority,
52601
+ recentBlockhash: blockhash,
52602
+ instructions: instructions2
52603
+ }).compileToV0Message(luts);
52604
+ const tx = new web3_js.VersionedTransaction(message);
52605
+ return addTransactionMetadata(tx, {
52606
+ signers: keys,
52607
+ addressLookupTables: luts,
52608
+ type: "DEPOSIT_STAKE" /* DEPOSIT_STAKE */
52609
+ });
52610
+ }
52611
+ async function makeRedeemStakedLstIx(params) {
52612
+ const { amount, authority, validator, connection } = params;
52613
+ const pool = findPoolAddress(validator);
52614
+ const lstMint = findPoolMintAddress(pool);
52615
+ const mintAuthority = findPoolMintAuthorityAddress(pool);
52616
+ const lstAta = getAssociatedTokenAddressSync(lstMint, authority);
52617
+ const rentExemption = await connection.getMinimumBalanceForRentExemption(
52618
+ web3_js.StakeProgram.space
52619
+ );
52620
+ const stakeAmount = new BigNumber3__default.default(new BigNumber3__default.default(amount).toString());
52621
+ const instructions2 = [];
52622
+ const signers = [];
52623
+ const stakeAccount = web3_js.Keypair.generate();
52624
+ signers.push(stakeAccount);
52625
+ instructions2.push(
52626
+ web3_js.SystemProgram.createAccount({
52627
+ fromPubkey: authority,
52628
+ newAccountPubkey: stakeAccount.publicKey,
52629
+ lamports: rentExemption,
52630
+ space: web3_js.StakeProgram.space,
52631
+ programId: web3_js.StakeProgram.programId
52632
+ })
52633
+ );
52634
+ instructions2.push(
52635
+ createApproveInstruction(
52636
+ lstAta,
52637
+ mintAuthority,
52638
+ authority,
52639
+ BigInt(stakeAmount.multipliedBy(1e9).toFixed(0))
52640
+ )
52641
+ );
52642
+ const withdrawStakeIx = await SinglePoolInstruction.withdrawStake(
52643
+ pool,
52644
+ stakeAccount.publicKey,
52645
+ authority,
52646
+ lstAta,
52647
+ stakeAmount
52648
+ );
52649
+ instructions2.push(withdrawStakeIx);
52650
+ return { instructions: instructions2, keys: signers };
52651
+ }
52652
+ async function makeRedeemStakedLstTx(params) {
52653
+ const { connection, luts, blockhash: providedBlockhash } = params;
52654
+ const { instructions: instructions2, keys } = await makeRedeemStakedLstIx(params);
52655
+ const blockhash = providedBlockhash ?? (await connection.getLatestBlockhash("confirmed")).blockhash;
52656
+ const message = new web3_js.TransactionMessage({
52657
+ payerKey: params.authority,
52658
+ recentBlockhash: blockhash,
52659
+ instructions: instructions2
52660
+ }).compileToV0Message(luts);
52661
+ const tx = new web3_js.VersionedTransaction(message);
52662
+ return addTransactionMetadata(tx, {
52663
+ signers: keys,
52664
+ addressLookupTables: luts,
52665
+ type: "WITHDRAW_STAKE" /* WITHDRAW_STAKE */
52666
+ });
52667
+ }
52668
+ async function makeMergeStakeAccountsTx(params) {
52669
+ const {
52670
+ authority,
52671
+ sourceStakeAccount,
52672
+ destinationStakeAccount,
52673
+ connection,
52674
+ luts,
52675
+ blockhash: providedBlockhash
52676
+ } = params;
52677
+ const mergeIx = web3_js.StakeProgram.merge({
52678
+ stakePubkey: destinationStakeAccount,
52679
+ sourceStakePubKey: sourceStakeAccount,
52680
+ authorizedPubkey: authority
52681
+ }).instructions;
52682
+ const blockhash = providedBlockhash ?? (await connection.getLatestBlockhash("confirmed")).blockhash;
52683
+ const message = new web3_js.TransactionMessage({
52684
+ payerKey: authority,
52685
+ recentBlockhash: blockhash,
52686
+ instructions: mergeIx
52687
+ }).compileToV0Message(luts);
52688
+ const tx = new web3_js.VersionedTransaction(message);
52689
+ return addTransactionMetadata(tx, {
52690
+ addressLookupTables: luts,
52691
+ type: "MERGE_STAKE_ACCOUNTS" /* MERGE_STAKE_ACCOUNTS */
52692
+ });
52693
+ }
51270
52694
  async function getKaminoMetadata(options) {
51271
52695
  const kaminoBanks = options.banks.filter((b) => b.config.assetTag === 3 /* KAMINO */);
51272
52696
  const DEFAULT_PUBKEY = web3_js.PublicKey.default;
@@ -52690,7 +54114,7 @@ var MarginfiAccount = class _MarginfiAccount {
52690
54114
  * @param params.oraclePrices - Map of current oracle prices
52691
54115
  * @param params.depositOpts - Deposit configuration (bank, amount, mode)
52692
54116
  * @param params.borrowOpts - Borrow configuration (bank, amount)
52693
- * @param params.swapOpts - Jupiter swap configuration (slippage, fees)
54117
+ * @param params.swapOpts - Swap configuration (venue, slippage, fees)
52694
54118
  * @param params.addressLookupTableAccounts - Address lookup tables
52695
54119
  * @param params.overrideInferAccounts - Optional account overrides
52696
54120
  * @param params.additionalIxs - Additional instructions to include
@@ -52728,7 +54152,7 @@ var MarginfiAccount = class _MarginfiAccount {
52728
54152
  * @param params.oraclePrices - Map of current oracle prices
52729
54153
  * @param params.withdrawOpts - Withdraw configuration (bank, amount)
52730
54154
  * @param params.repayOpts - Repay configuration (bank, optional amount)
52731
- * @param params.swapOpts - Jupiter swap configuration
54155
+ * @param params.swapOpts - Swap configuration (venue, slippage, fees)
52732
54156
  * @param params.addressLookupTableAccounts - Address lookup tables
52733
54157
  * @param params.overrideInferAccounts - Optional account overrides
52734
54158
  * @param params.additionalIxs - Additional instructions to include
@@ -52768,7 +54192,7 @@ var MarginfiAccount = class _MarginfiAccount {
52768
54192
  * @param params.oraclePrices - Map of current oracle prices
52769
54193
  * @param params.withdrawOpts - Withdraw configuration (bank, amount, tokenProgram)
52770
54194
  * @param params.depositOpts - Deposit configuration (bank, tokenProgram)
52771
- * @param params.swapOpts - Jupiter swap configuration
54195
+ * @param params.swapOpts - Swap configuration (venue, slippage, fees)
52772
54196
  * @param params.addressLookupTableAccounts - Address lookup tables
52773
54197
  * @param params.overrideInferAccounts - Optional account overrides
52774
54198
  * @param params.additionalIxs - Additional instructions to include
@@ -52808,7 +54232,7 @@ var MarginfiAccount = class _MarginfiAccount {
52808
54232
  * @param params.oraclePrices - Map of current oracle prices
52809
54233
  * @param params.repayOpts - Repay configuration (bank, amount, tokenProgram)
52810
54234
  * @param params.borrowOpts - Borrow configuration (bank, tokenProgram)
52811
- * @param params.swapOpts - Jupiter swap configuration
54235
+ * @param params.swapOpts - Swap configuration (venue, slippage, fees)
52812
54236
  * @param params.addressLookupTableAccounts - Address lookup tables
52813
54237
  * @param params.overrideInferAccounts - Optional account overrides
52814
54238
  * @param params.additionalIxs - Additional instructions to include
@@ -53703,66 +55127,6 @@ var MarginfiAccountWrapper = class {
53703
55127
  return this.account.getHealthCheckAccounts(this.client.bankMap, mandatoryBanks, excludedBanks);
53704
55128
  }
53705
55129
  // ----------------------------------------------------------------------------
53706
- // Native stake actions
53707
- // Note: These call standalone action functions directly rather than routing
53708
- // through this.account because they interact with the SPL stake pool program,
53709
- // not the marginfi program. No MarginfiAccount state is needed.
53710
- // ----------------------------------------------------------------------------
53711
- /**
53712
- * Creates a transaction to mint LST from a native stake account.
53713
- *
53714
- * Converts a native stake account (or a portion of it) into LST tokens
53715
- * by depositing the stake into the single-validator pool.
53716
- *
53717
- * @param amount - SOL amount to convert (in UI units)
53718
- * @param stakeAccountPk - The stake account to convert
53719
- * @param validator - The validator vote account
53720
- */
53721
- async makeMintStakedLstTx(amount, stakeAccountPk, validator) {
53722
- return makeMintStakedLstTx({
53723
- amount,
53724
- authority: this.authority,
53725
- stakeAccountPk,
53726
- validator,
53727
- connection: this.client.program.provider.connection,
53728
- luts: this.client.addressLookupTables
53729
- });
53730
- }
53731
- /**
53732
- * Creates a transaction to redeem LST tokens back to a native stake account.
53733
- *
53734
- * Burns LST tokens and withdraws the underlying stake into a new stake account.
53735
- *
53736
- * @param amount - LST amount to redeem (in UI units)
53737
- * @param validator - The validator vote account
53738
- */
53739
- async makeRedeemStakedLstTx(amount, validator) {
53740
- return makeRedeemStakedLstTx({
53741
- amount,
53742
- authority: this.authority,
53743
- validator,
53744
- connection: this.client.program.provider.connection,
53745
- luts: this.client.addressLookupTables
53746
- });
53747
- }
53748
- /**
53749
- * Creates a transaction to merge two stake accounts.
53750
- *
53751
- * Both accounts must share the same authorized staker/withdrawer and vote account.
53752
- *
53753
- * @param sourceStakeAccount - The stake account to merge from (will be consumed)
53754
- * @param destinationStakeAccount - The stake account to merge into
53755
- */
53756
- async makeMergeStakeAccountsTx(sourceStakeAccount, destinationStakeAccount) {
53757
- return makeMergeStakeAccountsTx({
53758
- authority: this.authority,
53759
- sourceStakeAccount,
53760
- destinationStakeAccount,
53761
- connection: this.client.program.provider.connection,
53762
- luts: this.client.addressLookupTables
53763
- });
53764
- }
53765
- // ----------------------------------------------------------------------------
53766
55130
  // Helper methods
53767
55131
  // ----------------------------------------------------------------------------
53768
55132
  /**
@@ -54032,56 +55396,11 @@ var Project0Client = class _Project0Client {
54032
55396
  break;
54033
55397
  }
54034
55398
  });
54035
- const stakedBanks = banksArray.filter((b) => b.config.assetTag === 2 /* STAKED */);
54036
- if (stakedBanks.length > 0) {
54037
- const metadataMap = getStakedBankMetadataMap();
54038
- const stakedBankAddresses = [];
54039
- const poolStakeAddresses = [];
54040
- const lstMintAddresses = [];
54041
- for (const bank of stakedBanks) {
54042
- const metadata = metadataMap.get(bank.address.toBase58());
54043
- if (!metadata) {
54044
- assetShareMultiplierByBank.set(bank.address.toBase58(), new BigNumber3__default.default(1));
54045
- continue;
54046
- }
54047
- const pool = findPoolAddress(new web3_js.PublicKey(metadata.validatorVoteAccount));
54048
- stakedBankAddresses.push(bank.address.toBase58());
54049
- poolStakeAddresses.push(findPoolStakeAddress(pool));
54050
- lstMintAddresses.push(findPoolMintAddress(pool));
54051
- }
54052
- if (stakedBankAddresses.length > 0) {
54053
- const allAddresses = [
54054
- ...poolStakeAddresses.map((a) => a.toBase58()),
54055
- ...lstMintAddresses.map((a) => a.toBase58())
54056
- ];
54057
- const accountInfos = await chunkedGetRawMultipleAccountInfoOrdered(
54058
- connection,
54059
- allAddresses
54060
- );
54061
- const poolStakeInfos = accountInfos.slice(0, poolStakeAddresses.length);
54062
- const lstMintInfos = accountInfos.slice(poolStakeAddresses.length);
54063
- for (let i = 0; i < stakedBankAddresses.length; i++) {
54064
- const bankAddr = stakedBankAddresses[i];
54065
- const poolStakeInfo = poolStakeInfos[i];
54066
- const lstMintInfo = lstMintInfos[i];
54067
- if (!poolStakeInfo || !lstMintInfo) {
54068
- assetShareMultiplierByBank.set(bankAddr, new BigNumber3__default.default(1));
54069
- continue;
54070
- }
54071
- const stakeLamports = poolStakeInfo.lamports;
54072
- const supplyBuffer = lstMintInfo.data.slice(36, 44);
54073
- const lstMintSupply = Number(Buffer.from(supplyBuffer).readBigUInt64LE(0));
54074
- if (lstMintSupply === 0) {
54075
- assetShareMultiplierByBank.set(bankAddr, new BigNumber3__default.default(1));
54076
- continue;
54077
- }
54078
- const LAMPORTS_PER_SOL5 = 1e9;
54079
- const adjustedStake = Math.max(stakeLamports - LAMPORTS_PER_SOL5, 0);
54080
- const multiplier = new BigNumber3__default.default(adjustedStake).dividedBy(lstMintSupply);
54081
- assetShareMultiplierByBank.set(bankAddr, multiplier);
54082
- }
54083
- }
54084
- }
55399
+ const stakedMultipliers = await computeStakedBankMultipliers(
55400
+ banksArray.filter((b) => b.config.assetTag === 2 /* STAKED */),
55401
+ connection
55402
+ );
55403
+ stakedMultipliers.forEach((v, k) => assetShareMultiplierByBank.set(k, v));
54085
55404
  const emodePairs = getEmodePairs(banksArray);
54086
55405
  return new _Project0Client(
54087
55406
  program,
@@ -54156,6 +55475,7 @@ exports.MARGINFI_PROGRAM = MARGINFI_PROGRAM;
54156
55475
  exports.MARGINFI_PROGRAM_STAGING = MARGINFI_PROGRAM_STAGING;
54157
55476
  exports.MARGINFI_PROGRAM_STAGING_ALT = MARGINFI_PROGRAM_STAGING_ALT;
54158
55477
  exports.MARGINFI_SPONSORED_SHARD_ID = MARGINFI_SPONSORED_SHARD_ID;
55478
+ exports.MAX_ACCOUNT_LOCKS = MAX_ACCOUNT_LOCKS;
54159
55479
  exports.MAX_CONFIDENCE_INTERVAL_RATIO = MAX_CONFIDENCE_INTERVAL_RATIO;
54160
55480
  exports.MAX_TX_SIZE = MAX_TX_SIZE;
54161
55481
  exports.MAX_U64 = MAX_U64;
@@ -54191,6 +55511,7 @@ exports.SYSTEM_PROGRAM_ID = SYSTEM_PROGRAM_ID;
54191
55511
  exports.SYSVAR_CLOCK_ID = SYSVAR_CLOCK_ID;
54192
55512
  exports.SYSVAR_RENT_ID = SYSVAR_RENT_ID;
54193
55513
  exports.SYSVAR_STAKE_HISTORY_ID = SYSVAR_STAKE_HISTORY_ID;
55514
+ exports.SwapProvider = SwapProvider;
54194
55515
  exports.TRANSFER_ACCOUNT_AUTHORITY_FLAG = TRANSFER_ACCOUNT_AUTHORITY_FLAG;
54195
55516
  exports.TransactionArenaKeyMap = TransactionArenaKeyMap;
54196
55517
  exports.TransactionBuildingError = TransactionBuildingError;
@@ -54224,6 +55545,7 @@ exports.checkMultipleOraclesCrankability = checkMultipleOraclesCrankability;
54224
55545
  exports.chunkedGetRawMultipleAccountInfoOrdered = chunkedGetRawMultipleAccountInfoOrdered;
54225
55546
  exports.chunkedGetRawMultipleAccountInfoOrderedWithNulls = chunkedGetRawMultipleAccountInfoOrderedWithNulls;
54226
55547
  exports.chunkedGetRawMultipleAccountInfos = chunkedGetRawMultipleAccountInfos;
55548
+ exports.compileFlashloanPrecheck = compileFlashloanPrecheck;
54227
55549
  exports.composeRemainingAccounts = composeRemainingAccounts;
54228
55550
  exports.computeAccountValue = computeAccountValue;
54229
55551
  exports.computeActiveEmodePairs = computeActiveEmodePairs;
@@ -54234,6 +55556,8 @@ exports.computeBaseInterestRate = computeBaseInterestRate;
54234
55556
  exports.computeClaimedEmissions = computeClaimedEmissions;
54235
55557
  exports.computeClosePositionTokenAmount = computeClosePositionTokenAmount;
54236
55558
  exports.computeEmodeImpacts = computeEmodeImpacts;
55559
+ exports.computeFlashLoanNonSwapBudget = computeFlashLoanNonSwapBudget;
55560
+ exports.computeFlashloanSwapConstraints = computeFlashloanSwapConstraints;
54237
55561
  exports.computeFreeCollateralFromBalances = computeFreeCollateralFromBalances;
54238
55562
  exports.computeFreeCollateralFromCache = computeFreeCollateralFromCache;
54239
55563
  exports.computeHealthAccountMetas = computeHealthAccountMetas;
@@ -54258,10 +55582,12 @@ exports.computeQuantity = computeQuantity;
54258
55582
  exports.computeQuantityUi = computeQuantityUi;
54259
55583
  exports.computeRemainingCapacity = computeRemainingCapacity;
54260
55584
  exports.computeSmartCrank = computeSmartCrank;
55585
+ exports.computeStakedBankMultipliers = computeStakedBankMultipliers;
54261
55586
  exports.computeTotalOutstandingEmissions = computeTotalOutstandingEmissions;
54262
55587
  exports.computeTvl = computeTvl;
54263
55588
  exports.computeUsdValue = computeUsdValue;
54264
55589
  exports.computeUtilizationRate = computeUtilizationRate;
55590
+ exports.computeV0TxSize = computeV0TxSize;
54265
55591
  exports.convertVoteAccCoeffsToBankCoeffs = convertVoteAccCoeffsToBankCoeffs;
54266
55592
  exports.createActiveEmodePairFromPairs = createActiveEmodePairFromPairs;
54267
55593
  exports.createEmptyBalance = createEmptyBalance;
@@ -54337,6 +55663,7 @@ exports.getDriftCTokenMultiplier = getDriftCTokenMultiplier;
54337
55663
  exports.getDriftMetadata = getDriftMetadata;
54338
55664
  exports.getDriftStatesDto = getDriftStatesDto;
54339
55665
  exports.getEmodePairs = getEmodePairs;
55666
+ exports.getExactOutEstimate = getExactOutEstimate;
54340
55667
  exports.getHealthCacheStatusDescription = getHealthCacheStatusDescription;
54341
55668
  exports.getHealthSimulationTransactions = getHealthSimulationTransactions;
54342
55669
  exports.getJupLendFTokenMultiplier = getJupLendFTokenMultiplier;
@@ -54355,10 +55682,15 @@ exports.getOracleSourceNameFromKey = getOracleSourceNameFromKey;
54355
55682
  exports.getPrice = getPrice;
54356
55683
  exports.getPriceWithConfidence = getPriceWithConfidence;
54357
55684
  exports.getStakedBankMetadataMap = getStakedBankMetadataMap;
55685
+ exports.getSwapIxsForFlashloan = getSwapIxsForFlashloan;
55686
+ exports.getTitanExactOutEstimate = getTitanExactOutEstimate;
55687
+ exports.getTitanSwapIxsForFlashloan = getTitanSwapIxsForFlashloan;
55688
+ exports.getTotalAccountKeys = getTotalAccountKeys;
54358
55689
  exports.getTotalAssetQuantity = getTotalAssetQuantity;
54359
55690
  exports.getTotalLiabilityQuantity = getTotalLiabilityQuantity;
54360
55691
  exports.getTxSize = getTxSize;
54361
55692
  exports.getValidatorVoteAccountByBank = getValidatorVoteAccountByBank;
55693
+ exports.getWritableAccountKeys = getWritableAccountKeys;
54362
55694
  exports.groupToDto = groupToDto;
54363
55695
  exports.hasAccountFlag = hasAccountFlag;
54364
55696
  exports.hasEmodeEntryFlag = hasEmodeEntryFlag;
@@ -54427,6 +55759,7 @@ exports.makeWithdrawIx = makeWithdrawIx3;
54427
55759
  exports.makeWithdrawTx = makeWithdrawTx;
54428
55760
  exports.makeWrapSolIxs = makeWrapSolIxs;
54429
55761
  exports.mapBrokenFeedsToOraclePrices = mapBrokenFeedsToOraclePrices;
55762
+ exports.mapJupiterQuoteToSwapQuoteResult = mapJupiterQuoteToSwapQuoteResult;
54430
55763
  exports.mapPythBanksToOraclePrices = mapPythBanksToOraclePrices;
54431
55764
  exports.mapSwbBanksToOraclePrices = mapSwbBanksToOraclePrices;
54432
55765
  exports.marginfiAccountToDto = marginfiAccountToDto;
@@ -54463,6 +55796,7 @@ exports.toBankDto = toBankDto;
54463
55796
  exports.toBigNumber = toBigNumber;
54464
55797
  exports.toEmodeSettingsDto = toEmodeSettingsDto;
54465
55798
  exports.toInterestRateConfigDto = toInterestRateConfigDto;
55799
+ exports.toJupiterConfig = toJupiterConfig;
54466
55800
  exports.toNumber = toNumber;
54467
55801
  exports.uiToNative = uiToNative;
54468
55802
  exports.uiToNativeBigNumber = uiToNativeBigNumber;