@0dotxyz/p0-ts-sdk 2.2.0-alpha.3 → 2.2.0-alpha.5

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
 
@@ -117,10 +120,14 @@ function getConfig(environment = "production", overrides) {
117
120
  var TransactionBuildingErrorCode = /* @__PURE__ */ ((TransactionBuildingErrorCode2) => {
118
121
  TransactionBuildingErrorCode2["JUPITER_SWAP_SIZE_EXCEEDED_REPAY"] = "JUPITER_SWAP_SIZE_EXCEEDED_REPAY";
119
122
  TransactionBuildingErrorCode2["JUPITER_SWAP_SIZE_EXCEEDED_LOOP"] = "JUPITER_SWAP_SIZE_EXCEEDED_LOOP";
123
+ TransactionBuildingErrorCode2["SWAP_SIZE_EXCEEDED_LOOP"] = "SWAP_SIZE_EXCEEDED_LOOP";
124
+ TransactionBuildingErrorCode2["SWAP_SIZE_EXCEEDED_REPAY"] = "SWAP_SIZE_EXCEEDED_REPAY";
120
125
  TransactionBuildingErrorCode2["ORACLE_CRANK_FAILED"] = "ORACLE_CRANK_FAILED";
121
126
  TransactionBuildingErrorCode2["KAMINO_RESERVE_NOT_FOUND"] = "KAMINO_RESERVE_NOT_FOUND";
122
127
  TransactionBuildingErrorCode2["DRIFT_STATE_NOT_FOUND"] = "DRIFT_STATE_NOT_FOUND";
123
128
  TransactionBuildingErrorCode2["JUPLEND_STATE_NOT_FOUND"] = "JUPLEND_STATE_NOT_FOUND";
129
+ TransactionBuildingErrorCode2["SWITCHBOARD_FEED_UPDATE_FAILED"] = "SWITCHBOARD_FEED_UPDATE_FAILED";
130
+ TransactionBuildingErrorCode2["SWAP_QUOTE_FAILED"] = "SWAP_QUOTE_FAILED";
124
131
  return TransactionBuildingErrorCode2;
125
132
  })(TransactionBuildingErrorCode || {});
126
133
  var TransactionBuildingError = class _TransactionBuildingError extends Error {
@@ -152,6 +159,20 @@ var TransactionBuildingError = class _TransactionBuildingError extends Error {
152
159
  { bytes, accountKeys }
153
160
  );
154
161
  }
162
+ static swapSizeExceededLoop(bytes, accountKeys, provider) {
163
+ return new _TransactionBuildingError(
164
+ "SWAP_SIZE_EXCEEDED_LOOP" /* SWAP_SIZE_EXCEEDED_LOOP */,
165
+ `${provider ?? "Swap"} instruction size exceeds available transaction size`,
166
+ { bytes, accountKeys, provider }
167
+ );
168
+ }
169
+ static swapSizeExceededRepay(bytes, accountKeys, provider) {
170
+ return new _TransactionBuildingError(
171
+ "SWAP_SIZE_EXCEEDED_REPAY" /* SWAP_SIZE_EXCEEDED_REPAY */,
172
+ `${provider ?? "Swap"} instruction size exceeds available transaction size`,
173
+ { bytes, accountKeys, provider }
174
+ );
175
+ }
155
176
  /**
156
177
  * Failed to crank oracles for one or more banks
157
178
  */
@@ -193,6 +214,26 @@ var TransactionBuildingError = class _TransactionBuildingError extends Error {
193
214
  { bankAddress, bankMint, bankSymbol }
194
215
  );
195
216
  }
217
+ /**
218
+ * Failed to update Switchboard price feeds
219
+ */
220
+ static switchboardFeedUpdateFailed(oracleKeys, reason) {
221
+ return new _TransactionBuildingError(
222
+ "SWITCHBOARD_FEED_UPDATE_FAILED" /* SWITCHBOARD_FEED_UPDATE_FAILED */,
223
+ `Switchboard feed update failed: ${reason}`,
224
+ { oracleKeys, reason }
225
+ );
226
+ }
227
+ /**
228
+ * Failed to get a swap quote from any provider
229
+ */
230
+ static swapQuoteFailed(provider, inputMint, outputMint, reason) {
231
+ return new _TransactionBuildingError(
232
+ "SWAP_QUOTE_FAILED" /* SWAP_QUOTE_FAILED */,
233
+ `${provider} swap quote failed for ${inputMint} \u2192 ${outputMint}: ${reason}`,
234
+ { provider, inputMint, outputMint, reason }
235
+ );
236
+ }
196
237
  /**
197
238
  * Generic escape hatch for custom errors
198
239
  */
@@ -345,6 +386,7 @@ var MAX_U64 = BigInt("18446744073709551615").toString();
345
386
 
346
387
  // src/constants/transaction.consts.ts
347
388
  var MAX_TX_SIZE = 1232;
389
+ var MAX_ACCOUNT_LOCKS = 64;
348
390
  var BUNDLE_TX_SIZE = 81;
349
391
  var PRIORITY_TX_SIZE = 44;
350
392
  var WSOL_MINT = new web3_js.PublicKey("So11111111111111111111111111111111111111112");
@@ -14893,8 +14935,40 @@ function getTxSize(tx) {
14893
14935
  const signaturesSize = (numRequiredSignatures - numSigners) * 64 + 1;
14894
14936
  try {
14895
14937
  const baseTxSize = isVersioned ? tx.serialize().length : tx.serialize({ requireAllSignatures: false, verifySignatures: false }).length;
14896
- return baseTxSize + feePayerSize + signaturesSize;
14897
- } catch {
14938
+ const totalSize = baseTxSize + feePayerSize + signaturesSize;
14939
+ if (isVersioned && totalSize > 1232) {
14940
+ const { header, staticAccountKeys, addressTableLookups } = tx.message;
14941
+ const lutWritable = addressTableLookups.reduce((s, l) => s + l.writableIndexes.length, 0);
14942
+ const lutReadonly = addressTableLookups.reduce((s, l) => s + l.readonlyIndexes.length, 0);
14943
+ console.warn("[getTxSize] oversized TX", {
14944
+ totalSize,
14945
+ overshoot: totalSize - 1232,
14946
+ staticKeys: staticAccountKeys.length,
14947
+ numSignatures: header.numRequiredSignatures,
14948
+ numLuts: addressTableLookups.length,
14949
+ lutWritable,
14950
+ lutReadonly,
14951
+ totalAccounts: staticAccountKeys.length + lutWritable + lutReadonly
14952
+ });
14953
+ }
14954
+ return totalSize;
14955
+ } catch (err) {
14956
+ if (isVersioned) {
14957
+ const { header, staticAccountKeys, addressTableLookups } = tx.message;
14958
+ const lutWritable = addressTableLookups.reduce((s, l) => s + l.writableIndexes.length, 0);
14959
+ const lutReadonly = addressTableLookups.reduce((s, l) => s + l.readonlyIndexes.length, 0);
14960
+ console.warn("[getTxSize] serialize failed", {
14961
+ error: err.message,
14962
+ staticKeys: staticAccountKeys.length,
14963
+ numSignatures: header.numRequiredSignatures,
14964
+ numLuts: addressTableLookups.length,
14965
+ lutWritable,
14966
+ lutReadonly,
14967
+ totalAccounts: staticAccountKeys.length + lutWritable + lutReadonly
14968
+ });
14969
+ } else {
14970
+ console.warn("[getTxSize] serialize failed", { error: err.message });
14971
+ }
14898
14972
  return 9999;
14899
14973
  }
14900
14974
  }
@@ -14909,6 +14983,47 @@ function getAccountKeys(tx, lookupTableAccounts) {
14909
14983
  } else {
14910
14984
  return tx.compileMessage().getAccountKeys().length;
14911
14985
  }
14986
+ } catch (err) {
14987
+ console.warn("[getAccountKeys] decompile failed", { error: err.message });
14988
+ return 9999;
14989
+ }
14990
+ }
14991
+ function getWritableAccountKeys(tx) {
14992
+ const isVersioned = isV0Tx(tx);
14993
+ try {
14994
+ if (isVersioned) {
14995
+ const { header, staticAccountKeys, addressTableLookups } = tx.message;
14996
+ const writableStatic = staticAccountKeys.length - header.numReadonlySignedAccounts - header.numReadonlyUnsignedAccounts;
14997
+ const writableLut = addressTableLookups.reduce(
14998
+ (sum, lookup) => sum + lookup.writableIndexes.length,
14999
+ 0
15000
+ );
15001
+ return writableStatic + writableLut;
15002
+ } else {
15003
+ const message = tx.compileMessage();
15004
+ const { numRequiredSignatures, numReadonlySignedAccounts, numReadonlyUnsignedAccounts } = message.header;
15005
+ const totalKeys = message.accountKeys.length;
15006
+ const writableSigned = numRequiredSignatures - numReadonlySignedAccounts;
15007
+ const writableUnsigned = totalKeys - numRequiredSignatures - numReadonlyUnsignedAccounts;
15008
+ return writableSigned + writableUnsigned;
15009
+ }
15010
+ } catch {
15011
+ return 9999;
15012
+ }
15013
+ }
15014
+ function getTotalAccountKeys(tx) {
15015
+ const isVersioned = isV0Tx(tx);
15016
+ try {
15017
+ if (isVersioned) {
15018
+ const { staticAccountKeys, addressTableLookups } = tx.message;
15019
+ const lutAccounts = addressTableLookups.reduce(
15020
+ (sum, lookup) => sum + lookup.writableIndexes.length + lookup.readonlyIndexes.length,
15021
+ 0
15022
+ );
15023
+ return staticAccountKeys.length + lutAccounts;
15024
+ } else {
15025
+ return tx.compileMessage().accountKeys.length;
15026
+ }
14912
15027
  } catch {
14913
15028
  return 9999;
14914
15029
  }
@@ -20146,6 +20261,14 @@ var HealthCacheSimulationError = class _HealthCacheSimulationError extends Error
20146
20261
  }
20147
20262
  };
20148
20263
 
20264
+ // src/services/account/types/action.types.ts
20265
+ var SwapProvider = /* @__PURE__ */ ((SwapProvider2) => {
20266
+ SwapProvider2["JUPITER"] = "JUPITER";
20267
+ SwapProvider2["TITAN"] = "TITAN";
20268
+ SwapProvider2["DFLOW"] = "DFLOW";
20269
+ return SwapProvider2;
20270
+ })(SwapProvider || {});
20271
+
20149
20272
  // src/services/account/utils/deserialize.utils.ts
20150
20273
  var EMPTY_HEALTH_CACHE = {
20151
20274
  assetValue: {
@@ -44194,6 +44317,330 @@ function makeUpdateJupLendRate({ lendingState }) {
44194
44317
  lendingState.rewardsRateModel
44195
44318
  );
44196
44319
  }
44320
+ var SUBPROTOCOL = "v1.api.titan.ag";
44321
+ var UINT64_MAX = (1n << 64n) - 1n;
44322
+ function toBigInt(value) {
44323
+ if (typeof value === "bigint") {
44324
+ if (value < 0n || value > UINT64_MAX) {
44325
+ throw new RangeError(`Amount out of uint64 range: ${value}`);
44326
+ }
44327
+ return value;
44328
+ }
44329
+ if (!Number.isInteger(value)) {
44330
+ throw new TypeError(`Amount must be a whole number, got ${value}`);
44331
+ }
44332
+ if (value < 0) {
44333
+ throw new RangeError(`Amount must be non-negative, got ${value}`);
44334
+ }
44335
+ return BigInt(value);
44336
+ }
44337
+ var ConnectionClosed = class _ConnectionClosed extends Error {
44338
+ code;
44339
+ reason;
44340
+ constructor(code, reason) {
44341
+ super(`Client WebSocket closed with code ${code}: ${reason}`);
44342
+ this.name = "ConnectionClosed";
44343
+ Object.setPrototypeOf(this, _ConnectionClosed.prototype);
44344
+ this.code = code;
44345
+ this.reason = reason;
44346
+ }
44347
+ };
44348
+ var ErrorResponse = class _ErrorResponse extends Error {
44349
+ response;
44350
+ constructor(response) {
44351
+ super(`Request ${response.requestId} failed with code ${response.code}: ${response.message}`);
44352
+ this.name = "ErrorResponse";
44353
+ Object.setPrototypeOf(this, _ErrorResponse.prototype);
44354
+ this.response = response;
44355
+ }
44356
+ };
44357
+ var StreamError = class _StreamError extends Error {
44358
+ streamId;
44359
+ errorCode;
44360
+ errorMessage;
44361
+ constructor(packet) {
44362
+ const code = packet.errorCode ?? 0;
44363
+ const message = packet.errorMessage ?? "";
44364
+ super(`Stream ${packet.id} ended with error code ${code}: ${message}`);
44365
+ this.name = "StreamError";
44366
+ Object.setPrototypeOf(this, _StreamError.prototype);
44367
+ this.streamId = packet.id;
44368
+ this.errorCode = code;
44369
+ this.errorMessage = message;
44370
+ }
44371
+ };
44372
+ var encoder = new msgpack.Encoder({ useBigInt64: true });
44373
+ var decoder = new msgpack.Decoder({ useBigInt64: true });
44374
+ var V1Client = class _V1Client {
44375
+ socket;
44376
+ nextId = 0;
44377
+ _closed = false;
44378
+ _closing = false;
44379
+ pending = /* @__PURE__ */ new Map();
44380
+ streams = /* @__PURE__ */ new Map();
44381
+ streamStopping = /* @__PURE__ */ new Map();
44382
+ closeListeners = [];
44383
+ // --- Static connect ---
44384
+ static connect(url) {
44385
+ return new Promise((resolve, reject) => {
44386
+ const ws = new WebSocket__default.default(url, [SUBPROTOCOL]);
44387
+ ws.binaryType = "arraybuffer";
44388
+ ws.on("open", () => {
44389
+ resolve(new _V1Client(ws));
44390
+ });
44391
+ ws.on("error", (err) => {
44392
+ reject(err);
44393
+ });
44394
+ });
44395
+ }
44396
+ // --- Constructor ---
44397
+ constructor(socket) {
44398
+ this.socket = socket;
44399
+ this.socket.on("message", (data) => {
44400
+ this.handleMessage(data);
44401
+ });
44402
+ this.socket.on("close", (code, reason) => {
44403
+ this.handleClose(code, reason.toString());
44404
+ });
44405
+ this.socket.on("error", (err) => {
44406
+ this.handleError(err);
44407
+ });
44408
+ }
44409
+ nextRequestId() {
44410
+ return this.nextId++;
44411
+ }
44412
+ // --- Public API ---
44413
+ get closed() {
44414
+ return this._closed;
44415
+ }
44416
+ close() {
44417
+ if (this._closed) return Promise.resolve();
44418
+ return new Promise((resolve, reject) => {
44419
+ this.closeListeners.push({ resolve, reject });
44420
+ if (!this._closing) {
44421
+ this._closing = true;
44422
+ this.socket.close();
44423
+ }
44424
+ });
44425
+ }
44426
+ newSwapQuoteStream(params) {
44427
+ const requestId = this.nextRequestId();
44428
+ const promise = new Promise(
44429
+ (resolve, reject) => {
44430
+ this.pending.set(requestId, {
44431
+ resolve,
44432
+ reject,
44433
+ kind: "NewSwapQuoteStream"
44434
+ });
44435
+ }
44436
+ );
44437
+ const normalized = {
44438
+ ...params,
44439
+ swap: { ...params.swap, amount: toBigInt(params.swap.amount) }
44440
+ };
44441
+ const message = {
44442
+ id: requestId,
44443
+ data: { NewSwapQuoteStream: normalized }
44444
+ };
44445
+ this.send(message);
44446
+ return promise;
44447
+ }
44448
+ stopStream(streamId) {
44449
+ const requestId = this.nextRequestId();
44450
+ const promise = new Promise((resolve, reject) => {
44451
+ this.pending.set(requestId, {
44452
+ resolve,
44453
+ reject,
44454
+ kind: "StopStream"
44455
+ });
44456
+ });
44457
+ const message = {
44458
+ id: requestId,
44459
+ data: { StopStream: { id: streamId } }
44460
+ };
44461
+ this.send(message);
44462
+ return promise;
44463
+ }
44464
+ // --- Send ---
44465
+ send(message) {
44466
+ try {
44467
+ const encoded = encoder.encode(message);
44468
+ this.socket.send(encoded);
44469
+ } catch (err) {
44470
+ const req = this.pending.get(message.id);
44471
+ if (req) {
44472
+ this.pending.delete(message.id);
44473
+ req.reject(err);
44474
+ }
44475
+ }
44476
+ }
44477
+ // --- Message handling ---
44478
+ handleMessage(raw) {
44479
+ let buf;
44480
+ if (raw instanceof ArrayBuffer) {
44481
+ buf = new Uint8Array(raw);
44482
+ } else if (Buffer.isBuffer(raw)) {
44483
+ buf = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
44484
+ } else if (Array.isArray(raw)) {
44485
+ buf = new Uint8Array(Buffer.concat(raw));
44486
+ } else {
44487
+ return;
44488
+ }
44489
+ let message;
44490
+ try {
44491
+ message = decoder.decode(buf);
44492
+ } catch {
44493
+ this.socket.close(3002, "failed to decode message");
44494
+ return;
44495
+ }
44496
+ if ("Response" in message) {
44497
+ this.handleResponse(message.Response);
44498
+ } else if ("Error" in message) {
44499
+ this.handleResponseError(message.Error);
44500
+ } else if ("StreamData" in message) {
44501
+ this.handleStreamData(message.StreamData);
44502
+ } else if ("StreamEnd" in message) {
44503
+ this.handleStreamEnd(message.StreamEnd);
44504
+ }
44505
+ }
44506
+ handleResponse(msg) {
44507
+ const req = this.pending.get(msg.requestId);
44508
+ if (!req) return;
44509
+ this.pending.delete(msg.requestId);
44510
+ if ("NewSwapQuoteStream" in msg.data && req.kind === "NewSwapQuoteStream") {
44511
+ const streamInfo = msg.stream;
44512
+ if (!streamInfo) {
44513
+ req.reject(new Error("No stream associated with NewSwapQuoteStream response"));
44514
+ return;
44515
+ }
44516
+ const stream = new ReadableStream({
44517
+ start: (controller) => {
44518
+ this.streams.set(streamInfo.id, controller);
44519
+ },
44520
+ cancel: () => {
44521
+ return this.cancelStream(streamInfo.id);
44522
+ }
44523
+ });
44524
+ const result = {
44525
+ response: msg.data.NewSwapQuoteStream,
44526
+ stream,
44527
+ streamId: streamInfo.id
44528
+ };
44529
+ req.resolve(result);
44530
+ } else if ("StreamStopped" in msg.data && req.kind === "StopStream") {
44531
+ req.resolve(msg.data.StreamStopped);
44532
+ } else {
44533
+ req.reject(new Error(`Unexpected response type for ${req.kind}`));
44534
+ }
44535
+ }
44536
+ handleResponseError(error) {
44537
+ const req = this.pending.get(error.requestId);
44538
+ if (!req) return;
44539
+ this.pending.delete(error.requestId);
44540
+ req.reject(new ErrorResponse(error));
44541
+ }
44542
+ handleStreamData(packet) {
44543
+ const controller = this.streams.get(packet.id);
44544
+ if (!controller) return;
44545
+ if (packet.payload.SwapQuotes !== void 0) {
44546
+ controller.enqueue(packet.payload.SwapQuotes);
44547
+ }
44548
+ }
44549
+ handleStreamEnd(packet) {
44550
+ const controller = this.streams.get(packet.id);
44551
+ if (!controller) return;
44552
+ this.streams.delete(packet.id);
44553
+ this.streamStopping.delete(packet.id);
44554
+ if (packet.errorCode !== void 0) {
44555
+ controller.error(new StreamError(packet));
44556
+ } else {
44557
+ controller.close();
44558
+ }
44559
+ }
44560
+ async cancelStream(streamId) {
44561
+ if (this.streamStopping.get(streamId) || !this.streams.has(streamId)) return;
44562
+ this.streamStopping.set(streamId, true);
44563
+ await this.stopStream(streamId);
44564
+ }
44565
+ // --- Connection lifecycle ---
44566
+ rejectAll(error) {
44567
+ for (const req of this.pending.values()) {
44568
+ req.reject(error);
44569
+ }
44570
+ this.pending.clear();
44571
+ for (const controller of this.streams.values()) {
44572
+ controller.error(error);
44573
+ }
44574
+ this.streams.clear();
44575
+ this.streamStopping.clear();
44576
+ }
44577
+ handleClose(code, reason) {
44578
+ this._closed = true;
44579
+ this.rejectAll(new ConnectionClosed(code, reason));
44580
+ for (const listener of this.closeListeners) {
44581
+ listener.resolve();
44582
+ }
44583
+ this.closeListeners = [];
44584
+ }
44585
+ handleError(err) {
44586
+ this.rejectAll(err);
44587
+ this.socket.close(3002);
44588
+ }
44589
+ };
44590
+ function deserializeSerializedInstruction(ix) {
44591
+ return new web3_js.TransactionInstruction({
44592
+ programId: new web3_js.PublicKey(Buffer.from(ix.p, "base64")),
44593
+ keys: ix.a.map((account) => ({
44594
+ pubkey: new web3_js.PublicKey(Buffer.from(account.p, "base64")),
44595
+ isSigner: account.s,
44596
+ isWritable: account.w
44597
+ })),
44598
+ data: Buffer.from(ix.d, "base64")
44599
+ });
44600
+ }
44601
+ function selectBestRoute(quotes, swapMode) {
44602
+ const routes = Object.values(quotes);
44603
+ if (routes.length === 0) return null;
44604
+ return routes.reduce((best, route) => {
44605
+ if (swapMode === "ExactIn") {
44606
+ return route.outAmount > best.outAmount ? route : best;
44607
+ } else {
44608
+ return route.inAmount < best.inAmount ? route : best;
44609
+ }
44610
+ });
44611
+ }
44612
+ function buildSwapQuoteResult(route, swapMode) {
44613
+ const slippageBps = route.slippageBps;
44614
+ let otherAmountThreshold;
44615
+ if (swapMode === "ExactIn") {
44616
+ otherAmountThreshold = String(Math.floor(route.outAmount * (1 - slippageBps / 1e4)));
44617
+ } else {
44618
+ otherAmountThreshold = String(Math.ceil(route.inAmount * (1 + slippageBps / 1e4)));
44619
+ }
44620
+ return {
44621
+ inAmount: String(route.inAmount),
44622
+ outAmount: String(route.outAmount),
44623
+ otherAmountThreshold,
44624
+ slippageBps,
44625
+ platformFee: route.platformFee ? {
44626
+ amount: String(route.platformFee.amount),
44627
+ feeBps: route.platformFee.fee_bps
44628
+ } : void 0,
44629
+ contextSlot: route.contextSlot,
44630
+ timeTaken: route.timeTaken
44631
+ };
44632
+ }
44633
+ async function resolveLookupTables(connection, lutPubkeys) {
44634
+ if (lutPubkeys.length === 0) return [];
44635
+ const lutAccountsRaw = await connection.getMultipleAccountsInfo(lutPubkeys);
44636
+ return lutAccountsRaw.map((accountInfo, index) => {
44637
+ if (!accountInfo) return null;
44638
+ return new web3_js.AddressLookupTableAccount({
44639
+ key: lutPubkeys[index],
44640
+ state: web3_js.AddressLookupTableAccount.deserialize(accountInfo.data)
44641
+ });
44642
+ }).filter((account) => account !== null);
44643
+ }
44197
44644
 
44198
44645
  // src/vendor/klend/utils/klend/interest-rate.utils.ts
44199
44646
  function getKaminoTotalSupply(reserve) {
@@ -46483,116 +46930,81 @@ async function makeJuplendDepositTx(params) {
46483
46930
  });
46484
46931
  return solanaTx;
46485
46932
  }
46486
- async function makeBeginFlashLoanIx3(program, marginfiAccountPk, endIndex, authority, isSync) {
46487
- const ix = isSync && authority ? sync_instructions_default.makeBeginFlashLoanIx(
46488
- program.programId,
46489
- {
46490
- marginfiAccount: marginfiAccountPk,
46491
- authority
46492
- },
46493
- { endIndex: new BN11__default.default(endIndex) }
46494
- ) : await instructions_default.makeBeginFlashLoanIx(
46933
+ async function makeRepayIx3({
46934
+ program,
46935
+ bank,
46936
+ tokenProgram,
46937
+ amount,
46938
+ authority,
46939
+ accountAddress,
46940
+ repayAll = false,
46941
+ isSync = false,
46942
+ opts = {}
46943
+ }) {
46944
+ const wrapAndUnwrapSol = opts.wrapAndUnwrapSol ?? true;
46945
+ const wSolBalanceUi = opts.wSolBalanceUi ?? 0;
46946
+ const repayIxs = [];
46947
+ const userAta = getAssociatedTokenAddressSync(bank.mint, authority, true, tokenProgram);
46948
+ const remainingAccounts = tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? [{ pubkey: bank.mint, isSigner: false, isWritable: false }] : [];
46949
+ if (bank.mint.equals(NATIVE_MINT) && wrapAndUnwrapSol) {
46950
+ repayIxs.push(...makeWrapSolIxs(authority, new BigNumber3.BigNumber(amount).minus(wSolBalanceUi)));
46951
+ }
46952
+ const repayIx = !isSync || !opts.overrideInferAccounts?.group ? await instructions_default.makeRepayIx(
46495
46953
  program,
46496
46954
  {
46497
- marginfiAccount: marginfiAccountPk,
46498
- authority
46955
+ marginfiAccount: accountAddress,
46956
+ signerTokenAccount: userAta,
46957
+ bank: bank.address,
46958
+ tokenProgram,
46959
+ authority: opts.overrideInferAccounts?.authority ?? authority,
46960
+ group: opts.overrideInferAccounts?.group,
46961
+ liquidityVault: opts.overrideInferAccounts?.liquidityVault
46499
46962
  },
46500
- { endIndex: new BN11__default.default(endIndex) }
46501
- );
46502
- return { instructions: [ix], keys: [] };
46503
- }
46504
- async function makeEndFlashLoanIx3(program, marginfiAccountPk, projectedActiveBanks, authority, isSync) {
46505
- const remainingAccounts = computeHealthAccountMetas(projectedActiveBanks);
46506
- const ix = isSync && authority ? sync_instructions_default.makeEndFlashLoanIx(
46963
+ { amount: uiToNative(amount, bank.mintDecimals), repayAll },
46964
+ remainingAccounts
46965
+ ) : sync_instructions_default.makeRepayIx(
46507
46966
  program.programId,
46508
46967
  {
46509
- marginfiAccount: marginfiAccountPk,
46510
- authority
46511
- },
46512
- remainingAccounts.map((account) => ({
46513
- pubkey: account,
46514
- isSigner: false,
46515
- isWritable: false
46516
- }))
46517
- ) : await instructions_default.makeEndFlashLoanIx(
46518
- program,
46519
- {
46520
- marginfiAccount: marginfiAccountPk,
46521
- authority
46968
+ marginfiAccount: accountAddress,
46969
+ signerTokenAccount: userAta,
46970
+ bank: bank.address,
46971
+ tokenProgram,
46972
+ authority: opts.overrideInferAccounts?.authority ?? authority,
46973
+ group: opts.overrideInferAccounts?.group
46522
46974
  },
46523
- remainingAccounts.map((account) => ({
46524
- pubkey: account,
46525
- isSigner: false,
46526
- isWritable: false
46527
- }))
46975
+ { amount: uiToNative(amount, bank.mintDecimals), repayAll },
46976
+ remainingAccounts
46528
46977
  );
46529
- return { instructions: [ix], keys: [] };
46978
+ repayIxs.push(repayIx);
46979
+ return {
46980
+ instructions: repayIxs,
46981
+ keys: []
46982
+ };
46530
46983
  }
46531
- async function makeFlashLoanTx({
46532
- program,
46533
- marginfiAccount,
46534
- ixs,
46535
- bankMap,
46536
- blockhash,
46537
- addressLookupTableAccounts,
46538
- signers,
46539
- isSync
46540
- }) {
46541
- const endIndex = ixs.length + 1;
46542
- const projectedActiveBanksKeys = computeProjectedActiveBanksNoCpi(
46543
- marginfiAccount.balances,
46544
- ixs,
46545
- program
46546
- );
46547
- const projectedActiveBanks = projectedActiveBanksKeys.map((account) => {
46548
- const b = bankMap.get(account.toBase58());
46549
- if (!b) throw Error(`Bank ${account.toBase58()} not found, in makeFlashLoanTx function`);
46550
- return b;
46551
- });
46552
- const beginFlashLoanIx = await makeBeginFlashLoanIx3(
46553
- program,
46554
- marginfiAccount.address,
46555
- endIndex,
46556
- marginfiAccount.authority,
46557
- isSync
46558
- );
46559
- const endFlashLoanIx = await makeEndFlashLoanIx3(
46560
- program,
46561
- marginfiAccount.address,
46562
- projectedActiveBanks,
46563
- marginfiAccount.authority,
46564
- isSync
46565
- );
46566
- const message = new web3_js.TransactionMessage({
46567
- payerKey: marginfiAccount.authority,
46568
- recentBlockhash: blockhash,
46569
- instructions: [...beginFlashLoanIx.instructions, ...ixs, ...endFlashLoanIx.instructions]
46570
- }).compileToV0Message(addressLookupTableAccounts);
46571
- const tx = addTransactionMetadata(new web3_js.VersionedTransaction(message), {
46572
- addressLookupTables: addressLookupTableAccounts,
46573
- type: "FLASHLOAN" /* FLASHLOAN */,
46574
- signers
46984
+ async function makeRepayTx(params) {
46985
+ const { luts, ...depositIxParams } = params;
46986
+ const ixs = await makeRepayIx3(depositIxParams);
46987
+ const tx = new web3_js.Transaction().add(...ixs.instructions);
46988
+ tx.feePayer = params.authority;
46989
+ const solanaTx = addTransactionMetadata(tx, {
46990
+ type: "REPAY" /* REPAY */,
46991
+ signers: ixs.keys,
46992
+ addressLookupTables: luts
46575
46993
  });
46576
- if (signers) {
46577
- tx.sign(signers);
46578
- }
46579
- return tx;
46994
+ return solanaTx;
46580
46995
  }
46581
-
46582
- // src/services/account/actions/loop.ts
46583
- async function makeLoopTx(params) {
46996
+ async function makeRepayWithCollatTx(params) {
46584
46997
  const {
46585
46998
  program,
46586
46999
  marginfiAccount,
46587
47000
  bankMap,
46588
- depositOpts,
46589
- borrowOpts,
47001
+ withdrawOpts,
47002
+ repayOpts,
46590
47003
  bankMetadataMap,
46591
47004
  addressLookupTableAccounts,
46592
47005
  connection,
46593
47006
  oraclePrices,
46594
- crossbarUrl,
46595
- additionalIxs = []
47007
+ crossbarUrl
46596
47008
  } = params;
46597
47009
  const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
46598
47010
  const setupIxs = await makeSetupIx({
@@ -46600,34 +47012,34 @@ async function makeLoopTx(params) {
46600
47012
  authority: marginfiAccount.authority,
46601
47013
  tokens: [
46602
47014
  {
46603
- mint: borrowOpts.borrowBank.mint,
46604
- tokenProgram: borrowOpts.tokenProgram
47015
+ mint: repayOpts.repayBank.mint,
47016
+ tokenProgram: repayOpts.tokenProgram
46605
47017
  },
46606
47018
  {
46607
- mint: depositOpts.depositBank.mint,
46608
- tokenProgram: depositOpts.tokenProgram
47019
+ mint: withdrawOpts.withdrawBank.mint,
47020
+ tokenProgram: withdrawOpts.tokenProgram
46609
47021
  }
46610
47022
  ]
46611
47023
  });
46612
- const updateJupLendRateIxs = makeUpdateJupLendRateIxs(
46613
- params.marginfiAccount,
46614
- params.bankMap,
46615
- [depositOpts.depositBank.address],
46616
- params.bankMetadataMap
47024
+ const updateJuplendMarketIxs = makeUpdateJupLendRateIxs(
47025
+ marginfiAccount,
47026
+ bankMap,
47027
+ [withdrawOpts.withdrawBank.address],
47028
+ bankMetadataMap
46617
47029
  );
46618
47030
  const updateDriftMarketIxs = makeUpdateDriftMarketIxs(
46619
- params.marginfiAccount,
46620
- params.bankMap,
46621
- [depositOpts.depositBank.address],
46622
- params.bankMetadataMap
47031
+ marginfiAccount,
47032
+ bankMap,
47033
+ [withdrawOpts.withdrawBank.address],
47034
+ bankMetadataMap
46623
47035
  );
46624
47036
  const kaminoRefreshIxs = makeRefreshKaminoBanksIxs(
46625
47037
  marginfiAccount,
46626
47038
  bankMap,
46627
- [borrowOpts.borrowBank.address, depositOpts.depositBank.address],
47039
+ [withdrawOpts.withdrawBank.address, repayOpts.repayBank.address],
46628
47040
  bankMetadataMap
46629
47041
  );
46630
- const { flashloanTx, setupInstructions, swapQuote, amountToDeposit, depositIxs, borrowIxs } = await buildLoopFlashloanTx({
47042
+ const { flashloanTx, setupInstructions, swapQuote, amountToRepay, withdrawIxs, repayIxs } = await buildRepayWithCollatFlashloanTx({
46631
47043
  ...params,
46632
47044
  blockhash
46633
47045
  });
@@ -46637,7 +47049,7 @@ async function makeLoopTx(params) {
46637
47049
  }
46638
47050
  if (ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
46639
47051
  const mintKey = ix.keys[3]?.pubkey;
46640
- if (mintKey?.equals(depositOpts.depositBank.mint) || mintKey?.equals(borrowOpts.borrowBank.mint)) {
47052
+ if (mintKey?.equals(withdrawOpts.withdrawBank.mint) || mintKey?.equals(repayOpts.repayBank.mint)) {
46641
47053
  return false;
46642
47054
  }
46643
47055
  }
@@ -46649,24 +47061,18 @@ async function makeLoopTx(params) {
46649
47061
  bankMap,
46650
47062
  oraclePrices,
46651
47063
  assetShareValueMultiplierByBank: params.assetShareValueMultiplierByBank,
46652
- instructions: [...borrowIxs.instructions, ...depositIxs.instructions],
47064
+ instructions: [...withdrawIxs.instructions, ...repayIxs.instructions],
46653
47065
  program,
46654
47066
  connection,
46655
47067
  crossbarUrl
46656
47068
  });
46657
47069
  let additionalTxs = [];
46658
- if (depositOpts.depositBank.mint.equals(NATIVE_MINT) && depositOpts.inputDepositAmount) {
46659
- setupIxs.push(
46660
- ...makeWrapSolIxs(marginfiAccount.authority, new BigNumber3.BigNumber(depositOpts.inputDepositAmount))
46661
- );
46662
- }
46663
- if (setupIxs.length > 0 || additionalIxs.length > 0 || kaminoRefreshIxs.instructions.length > 0 || updateDriftMarketIxs.instructions.length > 0 || updateJupLendRateIxs.instructions.length > 0) {
47070
+ if (setupIxs.length > 0 || kaminoRefreshIxs.instructions.length > 0 || updateDriftMarketIxs.instructions.length > 0 || updateJuplendMarketIxs.instructions.length > 0) {
46664
47071
  const ixs = [
46665
- ...additionalIxs,
46666
47072
  ...setupIxs,
46667
47073
  ...kaminoRefreshIxs.instructions,
46668
47074
  ...updateDriftMarketIxs.instructions,
46669
- ...updateJupLendRateIxs.instructions
47075
+ ...updateJuplendMarketIxs.instructions
46670
47076
  ];
46671
47077
  const txs = splitInstructionsToFitTransactions([], ixs, {
46672
47078
  blockhash,
@@ -46696,300 +47102,835 @@ async function makeLoopTx(params) {
46696
47102
  );
46697
47103
  }
46698
47104
  const transactions = [...additionalTxs, flashloanTx];
46699
- return {
46700
- transactions,
46701
- actionTxIndex: transactions.length - 1,
46702
- quoteResponse: swapQuote
46703
- };
47105
+ return { transactions, swapQuote, amountToRepay };
46704
47106
  }
46705
- async function buildLoopFlashloanTx({
47107
+ async function buildRepayWithCollatFlashloanTx({
46706
47108
  program,
46707
47109
  marginfiAccount,
46708
47110
  bankMap,
46709
- borrowOpts,
46710
- depositOpts,
47111
+ withdrawOpts,
47112
+ repayOpts,
46711
47113
  bankMetadataMap,
47114
+ assetShareValueMultiplierByBank,
46712
47115
  addressLookupTableAccounts,
46713
47116
  connection,
46714
47117
  swapOpts,
46715
47118
  overrideInferAccounts,
46716
47119
  blockhash
46717
47120
  }) {
46718
- const swapResult = [];
46719
47121
  const cuRequestIxs = [
46720
47122
  web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
46721
47123
  web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
46722
47124
  ];
46723
- 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
- });
47125
+ let amountToRepay;
47126
+ let swapInstructions = [];
47127
+ let setupInstructions = [];
47128
+ let swapLookupTables = [];
47129
+ let swapQuote;
47130
+ let sizeConstraintUsed = 0;
47131
+ if (repayOpts.repayBank.mint.equals(withdrawOpts.withdrawBank.mint)) {
47132
+ amountToRepay = withdrawOpts.withdrawAmount;
46730
47133
  } else {
46731
47134
  const destinationTokenAccount = getAssociatedTokenAddressSync(
46732
- new web3_js.PublicKey(depositOpts.depositBank.mint),
47135
+ new web3_js.PublicKey(repayOpts.repayBank.mint),
46733
47136
  marginfiAccount.authority,
46734
47137
  true,
46735
- depositOpts.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
47138
+ repayOpts.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
46736
47139
  );
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
47140
+ const swapConstraints = await computeFlashloanSwapConstraints({
47141
+ program,
47142
+ marginfiAccount,
47143
+ bankMap,
47144
+ bankMetadataMap,
47145
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
47146
+ primaryIx: {
47147
+ type: "withdraw",
47148
+ bank: withdrawOpts.withdrawBank,
47149
+ tokenProgram: withdrawOpts.tokenProgram
47150
+ },
47151
+ secondaryIx: {
47152
+ type: "repay",
47153
+ bank: repayOpts.repayBank,
47154
+ tokenProgram: repayOpts.tokenProgram
46747
47155
  },
47156
+ overrideInferAccounts
47157
+ });
47158
+ const swapResponse = await getSwapIxsForFlashloan({
47159
+ inputMint: withdrawOpts.withdrawBank.mint.toBase58(),
47160
+ outputMint: repayOpts.repayBank.mint.toBase58(),
47161
+ amount: uiToNative(
47162
+ withdrawOpts.withdrawAmount,
47163
+ withdrawOpts.withdrawBank.mintDecimals
47164
+ ).toNumber(),
47165
+ swapMode: "ExactIn",
46748
47166
  authority: marginfiAccount.authority,
46749
47167
  connection,
46750
47168
  destinationTokenAccount,
46751
- configParams: swapOpts.jupiterOptions?.configParams
47169
+ swapOpts,
47170
+ sizeConstraint: swapConstraints.sizeConstraint,
47171
+ maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
46752
47172
  });
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
47173
+ sizeConstraintUsed = swapConstraints.sizeConstraint;
47174
+ const { quoteResponse } = swapResponse;
47175
+ const outAmount = nativeToUi(quoteResponse.outAmount, repayOpts.repayBank.mintDecimals);
47176
+ const outAmountThreshold = nativeToUi(
47177
+ quoteResponse.otherAmountThreshold,
47178
+ repayOpts.repayBank.mintDecimals
47179
+ );
47180
+ amountToRepay = outAmount > repayOpts.totalPositionAmount ? repayOpts.totalPositionAmount : outAmountThreshold;
47181
+ swapInstructions = swapResponse.swapInstructions;
47182
+ swapLookupTables = swapResponse.addressLookupTableAddresses;
47183
+ swapQuote = quoteResponse;
47184
+ }
47185
+ let withdrawIxs;
47186
+ switch (withdrawOpts.withdrawBank.config.assetTag) {
47187
+ case 3 /* KAMINO */: {
47188
+ const reserve = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.kaminoStates?.reserveState;
47189
+ if (!reserve) {
47190
+ throw TransactionBuildingError.kaminoReserveNotFound(
47191
+ withdrawOpts.withdrawBank.address.toBase58(),
47192
+ withdrawOpts.withdrawBank.mint.toBase58(),
47193
+ withdrawOpts.withdrawBank.tokenSymbol
47194
+ );
47195
+ }
47196
+ const multiplier = assetShareValueMultiplierByBank.get(withdrawOpts.withdrawBank.address.toBase58()) ?? new BigNumber3.BigNumber(1);
47197
+ const adjustedAmount = new BigNumber3.BigNumber(withdrawOpts.withdrawAmount).div(multiplier).times(1.0001).toNumber();
47198
+ withdrawIxs = await makeKaminoWithdrawIx3({
47199
+ program,
47200
+ bank: withdrawOpts.withdrawBank,
47201
+ bankMap,
47202
+ tokenProgram: withdrawOpts.tokenProgram,
47203
+ cTokenAmount: adjustedAmount,
47204
+ marginfiAccount,
47205
+ authority: marginfiAccount.authority,
47206
+ reserve,
47207
+ withdrawAll: isWholePosition(
47208
+ {
47209
+ amount: withdrawOpts.totalPositionAmount,
47210
+ isLending: true
47211
+ },
47212
+ withdrawOpts.withdrawAmount,
47213
+ withdrawOpts.withdrawBank.mintDecimals
47214
+ ),
47215
+ isSync: false,
47216
+ opts: {
47217
+ createAtas: false,
47218
+ wrapAndUnwrapSol: false,
47219
+ overrideInferAccounts
47220
+ }
46765
47221
  });
46766
- });
47222
+ break;
47223
+ }
47224
+ case 4 /* DRIFT */: {
47225
+ const driftState = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.driftStates;
47226
+ if (!driftState) {
47227
+ throw TransactionBuildingError.driftStateNotFound(
47228
+ withdrawOpts.withdrawBank.address.toBase58(),
47229
+ withdrawOpts.withdrawBank.mint.toBase58(),
47230
+ withdrawOpts.withdrawBank.tokenSymbol
47231
+ );
47232
+ }
47233
+ withdrawIxs = await makeDriftWithdrawIx3({
47234
+ program,
47235
+ bank: withdrawOpts.withdrawBank,
47236
+ bankMap,
47237
+ tokenProgram: withdrawOpts.tokenProgram,
47238
+ amount: withdrawOpts.withdrawAmount,
47239
+ marginfiAccount,
47240
+ authority: marginfiAccount.authority,
47241
+ driftSpotMarket: driftState.spotMarketState,
47242
+ userRewards: driftState.userRewards,
47243
+ withdrawAll: isWholePosition(
47244
+ {
47245
+ amount: withdrawOpts.totalPositionAmount,
47246
+ isLending: true
47247
+ },
47248
+ withdrawOpts.withdrawAmount,
47249
+ withdrawOpts.withdrawBank.mintDecimals
47250
+ ),
47251
+ isSync: false,
47252
+ opts: {
47253
+ createAtas: false,
47254
+ wrapAndUnwrapSol: false,
47255
+ overrideInferAccounts
47256
+ }
47257
+ });
47258
+ break;
47259
+ }
47260
+ case 6 /* JUPLEND */: {
47261
+ const jupLendState = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.jupLendStates;
47262
+ if (!jupLendState) {
47263
+ throw TransactionBuildingError.jupLendStateNotFound(
47264
+ withdrawOpts.withdrawBank.address.toBase58(),
47265
+ withdrawOpts.withdrawBank.mint.toBase58(),
47266
+ withdrawOpts.withdrawBank.tokenSymbol
47267
+ );
47268
+ }
47269
+ withdrawIxs = await makeJuplendWithdrawIx2({
47270
+ program,
47271
+ bank: withdrawOpts.withdrawBank,
47272
+ bankMap,
47273
+ tokenProgram: withdrawOpts.tokenProgram,
47274
+ amount: withdrawOpts.withdrawAmount,
47275
+ marginfiAccount,
47276
+ authority: marginfiAccount.authority,
47277
+ jupLendingState: jupLendState.jupLendingState,
47278
+ withdrawAll: isWholePosition(
47279
+ {
47280
+ amount: withdrawOpts.totalPositionAmount,
47281
+ isLending: true
47282
+ },
47283
+ withdrawOpts.withdrawAmount,
47284
+ withdrawOpts.withdrawBank.mintDecimals
47285
+ ),
47286
+ opts: {
47287
+ createAtas: false,
47288
+ wrapAndUnwrapSol: false,
47289
+ overrideInferAccounts
47290
+ }
47291
+ });
47292
+ break;
47293
+ }
47294
+ default: {
47295
+ withdrawIxs = await makeWithdrawIx3({
47296
+ program,
47297
+ bank: withdrawOpts.withdrawBank,
47298
+ bankMap,
47299
+ tokenProgram: withdrawOpts.tokenProgram,
47300
+ amount: withdrawOpts.withdrawAmount,
47301
+ marginfiAccount,
47302
+ authority: marginfiAccount.authority,
47303
+ withdrawAll: isWholePosition(
47304
+ {
47305
+ amount: withdrawOpts.totalPositionAmount,
47306
+ isLending: true
47307
+ },
47308
+ withdrawOpts.withdrawAmount,
47309
+ withdrawOpts.withdrawBank.mintDecimals
47310
+ ),
47311
+ isSync: false,
47312
+ opts: {
47313
+ createAtas: false,
47314
+ wrapAndUnwrapSol: false,
47315
+ overrideInferAccounts
47316
+ }
47317
+ });
47318
+ break;
47319
+ }
46767
47320
  }
46768
- const borrowIxs = await makeBorrowIx3({
47321
+ const repayIxs = await makeRepayIx3({
46769
47322
  program,
46770
- bank: borrowOpts.borrowBank,
46771
- bankMap,
46772
- tokenProgram: borrowOpts.tokenProgram,
46773
- amount: borrowOpts.borrowAmount,
46774
- marginfiAccount,
47323
+ bank: repayOpts.repayBank,
47324
+ tokenProgram: repayOpts.tokenProgram,
47325
+ amount: amountToRepay,
47326
+ accountAddress: marginfiAccount.address,
46775
47327
  authority: marginfiAccount.authority,
47328
+ repayAll: isWholePosition(
47329
+ {
47330
+ amount: repayOpts.totalPositionAmount,
47331
+ isLending: true
47332
+ },
47333
+ amountToRepay,
47334
+ repayOpts.repayBank.mintDecimals
47335
+ ),
46776
47336
  isSync: false,
46777
47337
  opts: {
46778
- createAtas: false,
46779
47338
  wrapAndUnwrapSol: false,
46780
47339
  overrideInferAccounts
46781
47340
  }
46782
47341
  });
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;
46817
- }
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
- );
46826
- }
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;
47342
+ const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
47343
+ const allNonFlIxs = [
47344
+ ...cuRequestIxs,
47345
+ ...withdrawIxs.instructions,
47346
+ ...swapInstructions,
47347
+ ...repayIxs.instructions
47348
+ ];
47349
+ if (swapInstructions.length > 0) {
47350
+ compileFlashloanPrecheck({
47351
+ allIxs: allNonFlIxs,
47352
+ payer: marginfiAccount.authority,
47353
+ luts,
47354
+ sizeConstraint: sizeConstraintUsed,
47355
+ swapIxCount: swapInstructions.length,
47356
+ swapLutCount: swapLookupTables.length
47357
+ });
47358
+ }
47359
+ const flashloanTx = await makeFlashLoanTx({
47360
+ program,
47361
+ marginfiAccount,
47362
+ bankMap,
47363
+ addressLookupTableAccounts: luts,
47364
+ blockhash,
47365
+ ixs: allNonFlIxs,
47366
+ isSync: true
47367
+ });
47368
+ const txSize = getTxSize(flashloanTx);
47369
+ const totalKeys = getTotalAccountKeys(flashloanTx);
47370
+ if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
47371
+ throw TransactionBuildingError.swapSizeExceededRepay(
47372
+ txSize,
47373
+ totalKeys,
47374
+ swapOpts.swapConfig?.provider
47375
+ );
47376
+ }
47377
+ return {
47378
+ flashloanTx,
47379
+ setupInstructions,
47380
+ swapQuote,
47381
+ withdrawIxs,
47382
+ repayIxs,
47383
+ amountToRepay
47384
+ };
47385
+ }
47386
+
47387
+ // src/services/account/utils/flashloan-size.utils.ts
47388
+ var SWAP_MERGE_OVERHEAD = 150;
47389
+ var FL_IX_OVERHEAD = 52;
47390
+ function compactU16Size(n) {
47391
+ return n < 128 ? 1 : n < 16384 ? 2 : 3;
47392
+ }
47393
+ function computeV0TxSize(ixs, payerKey, luts) {
47394
+ const keyMap = /* @__PURE__ */ new Map();
47395
+ const payerStr = payerKey.toBase58();
47396
+ keyMap.set(payerStr, { isSigner: true, isWritable: true });
47397
+ const programIds = /* @__PURE__ */ new Set();
47398
+ for (const ix of ixs) {
47399
+ const progStr = ix.programId.toBase58();
47400
+ programIds.add(progStr);
47401
+ if (!keyMap.has(progStr)) {
47402
+ keyMap.set(progStr, { isSigner: false, isWritable: false });
47403
+ }
47404
+ for (const meta of ix.keys) {
47405
+ const keyStr = meta.pubkey.toBase58();
47406
+ const existing = keyMap.get(keyStr);
47407
+ if (existing) {
47408
+ existing.isSigner = existing.isSigner || meta.isSigner;
47409
+ existing.isWritable = existing.isWritable || meta.isWritable;
47410
+ } else {
47411
+ keyMap.set(keyStr, { isSigner: meta.isSigner, isWritable: meta.isWritable });
46861
47412
  }
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;
47413
+ }
47414
+ }
47415
+ const lutLookup = /* @__PURE__ */ new Map();
47416
+ for (let li = 0; li < luts.length; li++) {
47417
+ const addresses = luts[li].state.addresses;
47418
+ for (let ai = 0; ai < addresses.length; ai++) {
47419
+ const addrStr = addresses[ai].toBase58();
47420
+ if (!lutLookup.has(addrStr)) {
47421
+ lutLookup.set(addrStr, { lutIdx: li, addrIdx: ai });
46877
47422
  }
46878
47423
  }
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);
47424
+ }
47425
+ let numStaticKeys = 0;
47426
+ let numWritableStaticKeys = 0;
47427
+ const lutWritableIdxs = luts.map(() => /* @__PURE__ */ new Set());
47428
+ const lutReadonlyIdxs = luts.map(() => /* @__PURE__ */ new Set());
47429
+ for (const [keyStr, props] of keyMap) {
47430
+ if (props.isSigner || programIds.has(keyStr)) {
47431
+ numStaticKeys++;
47432
+ if (props.isWritable) numWritableStaticKeys++;
47433
+ continue;
47434
+ }
47435
+ const lutEntry = lutLookup.get(keyStr);
47436
+ if (lutEntry) {
47437
+ if (props.isWritable) {
47438
+ lutWritableIdxs[lutEntry.lutIdx].add(lutEntry.addrIdx);
46902
47439
  } else {
46903
- continue;
47440
+ lutReadonlyIdxs[lutEntry.lutIdx].add(lutEntry.addrIdx);
46904
47441
  }
46905
47442
  } else {
46906
- return {
46907
- flashloanTx,
46908
- setupInstructions,
46909
- swapQuote: quoteResponse,
46910
- borrowIxs,
46911
- depositIxs,
46912
- amountToDeposit
46913
- };
47443
+ numStaticKeys++;
47444
+ if (props.isWritable) numWritableStaticKeys++;
46914
47445
  }
46915
47446
  }
46916
- throw new Error("Failed to build repay with collateral flashloan tx");
46917
- }
46918
- async function makeRepayIx3({
47447
+ const fixedOverhead = 101;
47448
+ const staticKeysSection = compactU16Size(numStaticKeys) + numStaticKeys * 32;
47449
+ let ixSection = compactU16Size(ixs.length);
47450
+ for (const ix of ixs) {
47451
+ const numAccounts = ix.keys.length;
47452
+ ixSection += 1 + // programId index
47453
+ compactU16Size(numAccounts) + numAccounts + // account key indexes
47454
+ compactU16Size(ix.data.length) + ix.data.length;
47455
+ }
47456
+ let numUsedLuts = 0;
47457
+ let lutSection = 0;
47458
+ for (let li = 0; li < luts.length; li++) {
47459
+ const wCount = lutWritableIdxs[li].size;
47460
+ const rCount = lutReadonlyIdxs[li].size;
47461
+ if (wCount === 0 && rCount === 0) continue;
47462
+ numUsedLuts++;
47463
+ lutSection += 32 + // LUT address
47464
+ compactU16Size(wCount) + wCount + // writable indexes
47465
+ compactU16Size(rCount) + rCount;
47466
+ }
47467
+ lutSection += compactU16Size(numUsedLuts);
47468
+ let totalLutKeys = 0;
47469
+ for (let li = 0; li < luts.length; li++) {
47470
+ totalLutKeys += lutWritableIdxs[li].size + lutReadonlyIdxs[li].size;
47471
+ }
47472
+ const accountCount = numStaticKeys + totalLutKeys;
47473
+ let totalLutWritableKeys = 0;
47474
+ for (let li = 0; li < luts.length; li++) {
47475
+ totalLutWritableKeys += lutWritableIdxs[li].size;
47476
+ }
47477
+ const writableAccountCount = numWritableStaticKeys + totalLutWritableKeys;
47478
+ const size = fixedOverhead + staticKeysSection + ixSection + lutSection + 1;
47479
+ return { size, accountCount, writableAccountCount };
47480
+ }
47481
+ function computeFlashLoanNonSwapBudget({
46919
47482
  program,
46920
- bank,
46921
- tokenProgram,
46922
- amount,
46923
- authority,
46924
- accountAddress,
46925
- repayAll = false,
46926
- isSync = false,
46927
- opts = {}
47483
+ marginfiAccount,
47484
+ ixs,
47485
+ bankMap,
47486
+ addressLookupTableAccounts
46928
47487
  }) {
46929
- const wrapAndUnwrapSol = opts.wrapAndUnwrapSol ?? true;
46930
- const wSolBalanceUi = opts.wSolBalanceUi ?? 0;
46931
- const repayIxs = [];
46932
- const userAta = getAssociatedTokenAddressSync(bank.mint, authority, true, tokenProgram);
46933
- const remainingAccounts = tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? [{ pubkey: bank.mint, isSigner: false, isWritable: false }] : [];
46934
- if (bank.mint.equals(NATIVE_MINT) && wrapAndUnwrapSol) {
46935
- repayIxs.push(...makeWrapSolIxs(authority, new BigNumber3.BigNumber(amount).minus(wSolBalanceUi)));
47488
+ const projectedActiveBanksKeys = computeProjectedActiveBanksNoCpi(
47489
+ marginfiAccount.balances,
47490
+ ixs,
47491
+ program
47492
+ );
47493
+ const projectedActiveBanks = projectedActiveBanksKeys.map((key) => {
47494
+ const b = bankMap.get(key.toBase58());
47495
+ if (!b) throw new Error(`Bank ${key.toBase58()} not found in computeFlashLoanNonSwapBudget`);
47496
+ return b;
47497
+ });
47498
+ const endIndex = ixs.length + 1;
47499
+ const beginFlIx = sync_instructions_default.makeBeginFlashLoanIx(
47500
+ program.programId,
47501
+ { marginfiAccount: marginfiAccount.address, authority: marginfiAccount.authority },
47502
+ { endIndex: new BN11__default.default(endIndex) }
47503
+ );
47504
+ const endFlRemainingAccounts = computeHealthAccountMetas(projectedActiveBanks);
47505
+ const endFlIx = sync_instructions_default.makeEndFlashLoanIx(
47506
+ program.programId,
47507
+ { marginfiAccount: marginfiAccount.address, authority: marginfiAccount.authority },
47508
+ endFlRemainingAccounts.map((pubkey) => ({ pubkey, isSigner: false, isWritable: false }))
47509
+ );
47510
+ const allNonSwapIxs = [beginFlIx, ...ixs, endFlIx];
47511
+ const nonSwapMsg = new web3_js.TransactionMessage({
47512
+ payerKey: marginfiAccount.authority,
47513
+ recentBlockhash: web3_js.PublicKey.default.toBase58(),
47514
+ instructions: allNonSwapIxs
47515
+ }).compileToV0Message(addressLookupTableAccounts);
47516
+ const nonSwapSize = new web3_js.VersionedTransaction(nonSwapMsg).serialize().length;
47517
+ const { header, staticAccountKeys, addressTableLookups } = nonSwapMsg;
47518
+ const nonSwapTotal = staticAccountKeys.length + addressTableLookups.reduce(
47519
+ (s, l) => s + l.writableIndexes.length + l.readonlyIndexes.length,
47520
+ 0
47521
+ );
47522
+ const sizeConstraint = MAX_TX_SIZE - nonSwapSize - SWAP_MERGE_OVERHEAD;
47523
+ const maxSwapTotalAccounts = MAX_ACCOUNT_LOCKS - nonSwapTotal;
47524
+ console.log("[flashloan-budget]", {
47525
+ method: "compiled",
47526
+ nonSwapSize,
47527
+ nonSwapTotal,
47528
+ sizeConstraint,
47529
+ maxSwapTotalAccounts
47530
+ });
47531
+ return { sizeConstraint, maxSwapTotalAccounts };
47532
+ }
47533
+ function compileFlashloanPrecheck({
47534
+ allIxs,
47535
+ payer,
47536
+ luts,
47537
+ sizeConstraint,
47538
+ swapIxCount,
47539
+ swapLutCount
47540
+ }) {
47541
+ const msg = new web3_js.TransactionMessage({
47542
+ payerKey: payer,
47543
+ recentBlockhash: web3_js.PublicKey.default.toBase58(),
47544
+ instructions: allIxs
47545
+ }).compileToV0Message(luts);
47546
+ const rawSize = new web3_js.VersionedTransaction(msg).serialize().length;
47547
+ const fullTxSize = rawSize + FL_IX_OVERHEAD;
47548
+ const overshoot = fullTxSize - MAX_TX_SIZE;
47549
+ const { header, staticAccountKeys, addressTableLookups } = msg;
47550
+ const writableStatic = staticAccountKeys.length - header.numReadonlySignedAccounts - header.numReadonlyUnsignedAccounts;
47551
+ const writableLut = addressTableLookups.reduce((s, l) => s + l.writableIndexes.length, 0);
47552
+ const writableAccounts = writableStatic + writableLut;
47553
+ const totalAccounts = staticAccountKeys.length + addressTableLookups.reduce(
47554
+ (s, l) => s + l.writableIndexes.length + l.readonlyIndexes.length,
47555
+ 0
47556
+ );
47557
+ console.log("[flashloan-precheck]", {
47558
+ fullTxSize,
47559
+ overshoot,
47560
+ sizeConstraint,
47561
+ writableAccounts,
47562
+ totalAccounts,
47563
+ staticKeys: staticAccountKeys.length,
47564
+ numLuts: addressTableLookups.length,
47565
+ swapIxCount,
47566
+ swapLutCount
47567
+ });
47568
+ return { fullTxSize, overshoot, writableAccounts, totalAccounts };
47569
+ }
47570
+ async function buildBudgetIx(config, program, marginfiAccount, bankMap, bankMetadataMap, overrideInferAccounts) {
47571
+ const { bank, tokenProgram } = config;
47572
+ switch (config.type) {
47573
+ case "borrow":
47574
+ return makeBorrowIx3({
47575
+ program,
47576
+ bank,
47577
+ bankMap,
47578
+ tokenProgram,
47579
+ amount: 1,
47580
+ marginfiAccount,
47581
+ authority: marginfiAccount.authority,
47582
+ isSync: true,
47583
+ opts: { createAtas: false, wrapAndUnwrapSol: false, overrideInferAccounts }
47584
+ });
47585
+ case "repay":
47586
+ return makeRepayIx3({
47587
+ program,
47588
+ bank,
47589
+ tokenProgram,
47590
+ amount: 1,
47591
+ accountAddress: marginfiAccount.address,
47592
+ authority: marginfiAccount.authority,
47593
+ repayAll: false,
47594
+ isSync: true,
47595
+ opts: { wrapAndUnwrapSol: false, overrideInferAccounts }
47596
+ });
47597
+ case "deposit":
47598
+ return buildDepositBudgetIx(
47599
+ config,
47600
+ program,
47601
+ marginfiAccount,
47602
+ bankMetadataMap,
47603
+ overrideInferAccounts
47604
+ );
47605
+ case "withdraw":
47606
+ return buildWithdrawBudgetIx(
47607
+ config,
47608
+ program,
47609
+ marginfiAccount,
47610
+ bankMap,
47611
+ bankMetadataMap,
47612
+ overrideInferAccounts
47613
+ );
46936
47614
  }
46937
- const repayIx = !isSync || !opts.overrideInferAccounts?.group ? await instructions_default.makeRepayIx(
47615
+ }
47616
+ async function buildDepositBudgetIx(config, program, marginfiAccount, bankMetadataMap, overrideInferAccounts) {
47617
+ const { bank, tokenProgram } = config;
47618
+ const opts = { wrapAndUnwrapSol: false, overrideInferAccounts };
47619
+ switch (bank.config.assetTag) {
47620
+ case 3 /* KAMINO */: {
47621
+ const reserve = bankMetadataMap[bank.address.toBase58()]?.kaminoStates?.reserveState;
47622
+ if (!reserve) {
47623
+ throw TransactionBuildingError.kaminoReserveNotFound(
47624
+ bank.address.toBase58(),
47625
+ bank.mint.toBase58(),
47626
+ bank.tokenSymbol
47627
+ );
47628
+ }
47629
+ return makeKaminoDepositIx3({
47630
+ program,
47631
+ bank,
47632
+ tokenProgram,
47633
+ amount: 1,
47634
+ accountAddress: marginfiAccount.address,
47635
+ authority: marginfiAccount.authority,
47636
+ group: marginfiAccount.group,
47637
+ reserve,
47638
+ isSync: true,
47639
+ opts
47640
+ });
47641
+ }
47642
+ case 4 /* DRIFT */: {
47643
+ const driftState = bankMetadataMap[bank.address.toBase58()]?.driftStates;
47644
+ if (!driftState) {
47645
+ throw TransactionBuildingError.driftStateNotFound(
47646
+ bank.address.toBase58(),
47647
+ bank.mint.toBase58(),
47648
+ bank.tokenSymbol
47649
+ );
47650
+ }
47651
+ return makeDriftDepositIx3({
47652
+ program,
47653
+ bank,
47654
+ tokenProgram,
47655
+ amount: 1,
47656
+ accountAddress: marginfiAccount.address,
47657
+ authority: marginfiAccount.authority,
47658
+ group: marginfiAccount.group,
47659
+ driftMarketIndex: driftState.spotMarketState.marketIndex,
47660
+ driftOracle: driftState.spotMarketState.oracle,
47661
+ isSync: true,
47662
+ opts
47663
+ });
47664
+ }
47665
+ case 6 /* JUPLEND */: {
47666
+ return makeJuplendDepositIx2({
47667
+ program,
47668
+ bank,
47669
+ tokenProgram,
47670
+ amount: 1,
47671
+ accountAddress: marginfiAccount.address,
47672
+ authority: marginfiAccount.authority,
47673
+ group: marginfiAccount.group,
47674
+ isSync: true,
47675
+ opts
47676
+ });
47677
+ }
47678
+ default: {
47679
+ return makeDepositIx3({
47680
+ program,
47681
+ bank,
47682
+ tokenProgram,
47683
+ amount: 1,
47684
+ accountAddress: marginfiAccount.address,
47685
+ authority: marginfiAccount.authority,
47686
+ group: marginfiAccount.group,
47687
+ isSync: true,
47688
+ opts
47689
+ });
47690
+ }
47691
+ }
47692
+ }
47693
+ async function buildWithdrawBudgetIx(config, program, marginfiAccount, bankMap, bankMetadataMap, overrideInferAccounts) {
47694
+ const { bank, tokenProgram } = config;
47695
+ const opts = { createAtas: false, wrapAndUnwrapSol: false, overrideInferAccounts };
47696
+ switch (bank.config.assetTag) {
47697
+ case 3 /* KAMINO */: {
47698
+ const reserve = bankMetadataMap[bank.address.toBase58()]?.kaminoStates?.reserveState;
47699
+ if (!reserve) {
47700
+ throw TransactionBuildingError.kaminoReserveNotFound(
47701
+ bank.address.toBase58(),
47702
+ bank.mint.toBase58(),
47703
+ bank.tokenSymbol
47704
+ );
47705
+ }
47706
+ return makeKaminoWithdrawIx3({
47707
+ program,
47708
+ bank,
47709
+ bankMap,
47710
+ tokenProgram,
47711
+ cTokenAmount: 1,
47712
+ marginfiAccount,
47713
+ authority: marginfiAccount.authority,
47714
+ reserve,
47715
+ withdrawAll: false,
47716
+ isSync: true,
47717
+ opts
47718
+ });
47719
+ }
47720
+ case 4 /* DRIFT */: {
47721
+ const driftState = bankMetadataMap[bank.address.toBase58()]?.driftStates;
47722
+ if (!driftState) {
47723
+ throw TransactionBuildingError.driftStateNotFound(
47724
+ bank.address.toBase58(),
47725
+ bank.mint.toBase58(),
47726
+ bank.tokenSymbol
47727
+ );
47728
+ }
47729
+ return makeDriftWithdrawIx3({
47730
+ program,
47731
+ bank,
47732
+ bankMap,
47733
+ tokenProgram,
47734
+ amount: 1,
47735
+ marginfiAccount,
47736
+ authority: marginfiAccount.authority,
47737
+ driftSpotMarket: driftState.spotMarketState,
47738
+ userRewards: driftState.userRewards,
47739
+ withdrawAll: false,
47740
+ isSync: true,
47741
+ opts
47742
+ });
47743
+ }
47744
+ case 6 /* JUPLEND */: {
47745
+ const jupLendState = bankMetadataMap[bank.address.toBase58()]?.jupLendStates;
47746
+ if (!jupLendState) {
47747
+ throw TransactionBuildingError.jupLendStateNotFound(
47748
+ bank.address.toBase58(),
47749
+ bank.mint.toBase58(),
47750
+ bank.tokenSymbol
47751
+ );
47752
+ }
47753
+ return makeJuplendWithdrawIx2({
47754
+ program,
47755
+ bank,
47756
+ bankMap,
47757
+ tokenProgram,
47758
+ amount: 1,
47759
+ marginfiAccount,
47760
+ authority: marginfiAccount.authority,
47761
+ jupLendingState: jupLendState.jupLendingState,
47762
+ withdrawAll: false,
47763
+ opts
47764
+ });
47765
+ }
47766
+ default: {
47767
+ return makeWithdrawIx3({
47768
+ program,
47769
+ bank,
47770
+ bankMap,
47771
+ tokenProgram,
47772
+ amount: 1,
47773
+ marginfiAccount,
47774
+ authority: marginfiAccount.authority,
47775
+ withdrawAll: false,
47776
+ isSync: true,
47777
+ opts
47778
+ });
47779
+ }
47780
+ }
47781
+ }
47782
+ async function computeFlashloanSwapConstraints({
47783
+ program,
47784
+ marginfiAccount,
47785
+ bankMap,
47786
+ addressLookupTableAccounts,
47787
+ bankMetadataMap,
47788
+ primaryIx,
47789
+ secondaryIx,
47790
+ overrideInferAccounts
47791
+ }) {
47792
+ const cuRequestIxs = [
47793
+ web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
47794
+ web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
47795
+ ];
47796
+ const [primaryResult, secondaryResult] = await Promise.all([
47797
+ buildBudgetIx(
47798
+ primaryIx,
47799
+ program,
47800
+ marginfiAccount,
47801
+ bankMap,
47802
+ bankMetadataMap,
47803
+ overrideInferAccounts
47804
+ ),
47805
+ buildBudgetIx(
47806
+ secondaryIx,
47807
+ program,
47808
+ marginfiAccount,
47809
+ bankMap,
47810
+ bankMetadataMap,
47811
+ overrideInferAccounts
47812
+ )
47813
+ ]);
47814
+ return computeFlashLoanNonSwapBudget({
46938
47815
  program,
47816
+ marginfiAccount,
47817
+ bankMap,
47818
+ addressLookupTableAccounts,
47819
+ ixs: [...cuRequestIxs, ...primaryResult.instructions, ...secondaryResult.instructions]
47820
+ });
47821
+ }
47822
+
47823
+ // src/services/account/actions/flash-loan.ts
47824
+ async function makeBeginFlashLoanIx3(program, marginfiAccountPk, endIndex, authority, isSync) {
47825
+ const ix = isSync && authority ? sync_instructions_default.makeBeginFlashLoanIx(
47826
+ program.programId,
46939
47827
  {
46940
- marginfiAccount: accountAddress,
46941
- signerTokenAccount: userAta,
46942
- bank: bank.address,
46943
- tokenProgram,
46944
- authority: opts.overrideInferAccounts?.authority ?? authority,
46945
- group: opts.overrideInferAccounts?.group,
46946
- liquidityVault: opts.overrideInferAccounts?.liquidityVault
47828
+ marginfiAccount: marginfiAccountPk,
47829
+ authority
46947
47830
  },
46948
- { amount: uiToNative(amount, bank.mintDecimals), repayAll },
46949
- remainingAccounts
46950
- ) : sync_instructions_default.makeRepayIx(
47831
+ { endIndex: new BN11__default.default(endIndex) }
47832
+ ) : await instructions_default.makeBeginFlashLoanIx(
47833
+ program,
47834
+ {
47835
+ marginfiAccount: marginfiAccountPk,
47836
+ authority
47837
+ },
47838
+ { endIndex: new BN11__default.default(endIndex) }
47839
+ );
47840
+ return { instructions: [ix], keys: [] };
47841
+ }
47842
+ async function makeEndFlashLoanIx3(program, marginfiAccountPk, projectedActiveBanks, authority, isSync) {
47843
+ const remainingAccounts = computeHealthAccountMetas(projectedActiveBanks);
47844
+ const ix = isSync && authority ? sync_instructions_default.makeEndFlashLoanIx(
46951
47845
  program.programId,
46952
47846
  {
46953
- marginfiAccount: accountAddress,
46954
- signerTokenAccount: userAta,
46955
- bank: bank.address,
46956
- tokenProgram,
46957
- authority: opts.overrideInferAccounts?.authority ?? authority,
46958
- group: opts.overrideInferAccounts?.group
47847
+ marginfiAccount: marginfiAccountPk,
47848
+ authority
46959
47849
  },
46960
- { amount: uiToNative(amount, bank.mintDecimals), repayAll },
46961
- remainingAccounts
47850
+ remainingAccounts.map((account) => ({
47851
+ pubkey: account,
47852
+ isSigner: false,
47853
+ isWritable: false
47854
+ }))
47855
+ ) : await instructions_default.makeEndFlashLoanIx(
47856
+ program,
47857
+ {
47858
+ marginfiAccount: marginfiAccountPk,
47859
+ authority
47860
+ },
47861
+ remainingAccounts.map((account) => ({
47862
+ pubkey: account,
47863
+ isSigner: false,
47864
+ isWritable: false
47865
+ }))
46962
47866
  );
46963
- repayIxs.push(repayIx);
46964
- return {
46965
- instructions: repayIxs,
46966
- keys: []
46967
- };
47867
+ return { instructions: [ix], keys: [] };
46968
47868
  }
46969
- async function makeRepayTx(params) {
46970
- const { luts, ...depositIxParams } = params;
46971
- const ixs = await makeRepayIx3(depositIxParams);
46972
- const tx = new web3_js.Transaction().add(...ixs.instructions);
46973
- tx.feePayer = params.authority;
46974
- const solanaTx = addTransactionMetadata(tx, {
46975
- type: "REPAY" /* REPAY */,
46976
- signers: ixs.keys,
46977
- addressLookupTables: luts
47869
+ async function makeFlashLoanTx({
47870
+ program,
47871
+ marginfiAccount,
47872
+ ixs,
47873
+ bankMap,
47874
+ blockhash,
47875
+ addressLookupTableAccounts,
47876
+ signers,
47877
+ isSync
47878
+ }) {
47879
+ const endIndex = ixs.length + 1;
47880
+ const projectedActiveBanksKeys = computeProjectedActiveBanksNoCpi(
47881
+ marginfiAccount.balances,
47882
+ ixs,
47883
+ program
47884
+ );
47885
+ const projectedActiveBanks = projectedActiveBanksKeys.map((account) => {
47886
+ const b = bankMap.get(account.toBase58());
47887
+ if (!b) throw Error(`Bank ${account.toBase58()} not found, in makeFlashLoanTx function`);
47888
+ return b;
46978
47889
  });
46979
- return solanaTx;
47890
+ const beginFlashLoanIx = await makeBeginFlashLoanIx3(
47891
+ program,
47892
+ marginfiAccount.address,
47893
+ endIndex,
47894
+ marginfiAccount.authority,
47895
+ isSync
47896
+ );
47897
+ const endFlashLoanIx = await makeEndFlashLoanIx3(
47898
+ program,
47899
+ marginfiAccount.address,
47900
+ projectedActiveBanks,
47901
+ marginfiAccount.authority,
47902
+ isSync
47903
+ );
47904
+ const message = new web3_js.TransactionMessage({
47905
+ payerKey: marginfiAccount.authority,
47906
+ recentBlockhash: blockhash,
47907
+ instructions: [...beginFlashLoanIx.instructions, ...ixs, ...endFlashLoanIx.instructions]
47908
+ }).compileToV0Message(addressLookupTableAccounts);
47909
+ const tx = addTransactionMetadata(new web3_js.VersionedTransaction(message), {
47910
+ addressLookupTables: addressLookupTableAccounts,
47911
+ type: "FLASHLOAN" /* FLASHLOAN */,
47912
+ signers
47913
+ });
47914
+ if (signers) {
47915
+ tx.sign(signers);
47916
+ }
47917
+ return tx;
46980
47918
  }
46981
- async function makeRepayWithCollatTx(params) {
47919
+
47920
+ // src/services/account/actions/loop.ts
47921
+ async function makeLoopTx(params) {
46982
47922
  const {
46983
47923
  program,
46984
47924
  marginfiAccount,
46985
47925
  bankMap,
46986
- withdrawOpts,
46987
- repayOpts,
47926
+ depositOpts,
47927
+ borrowOpts,
46988
47928
  bankMetadataMap,
46989
47929
  addressLookupTableAccounts,
46990
47930
  connection,
46991
47931
  oraclePrices,
46992
- crossbarUrl
47932
+ crossbarUrl,
47933
+ additionalIxs = []
46993
47934
  } = params;
46994
47935
  const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
46995
47936
  const setupIxs = await makeSetupIx({
@@ -46997,34 +47938,34 @@ async function makeRepayWithCollatTx(params) {
46997
47938
  authority: marginfiAccount.authority,
46998
47939
  tokens: [
46999
47940
  {
47000
- mint: repayOpts.repayBank.mint,
47001
- tokenProgram: repayOpts.tokenProgram
47941
+ mint: borrowOpts.borrowBank.mint,
47942
+ tokenProgram: borrowOpts.tokenProgram
47002
47943
  },
47003
47944
  {
47004
- mint: withdrawOpts.withdrawBank.mint,
47005
- tokenProgram: withdrawOpts.tokenProgram
47945
+ mint: depositOpts.depositBank.mint,
47946
+ tokenProgram: depositOpts.tokenProgram
47006
47947
  }
47007
47948
  ]
47008
47949
  });
47009
- const updateJuplendMarketIxs = makeUpdateJupLendRateIxs(
47010
- marginfiAccount,
47011
- bankMap,
47012
- [withdrawOpts.withdrawBank.address],
47013
- bankMetadataMap
47950
+ const updateJupLendRateIxs = makeUpdateJupLendRateIxs(
47951
+ params.marginfiAccount,
47952
+ params.bankMap,
47953
+ [depositOpts.depositBank.address],
47954
+ params.bankMetadataMap
47014
47955
  );
47015
47956
  const updateDriftMarketIxs = makeUpdateDriftMarketIxs(
47016
- marginfiAccount,
47017
- bankMap,
47018
- [withdrawOpts.withdrawBank.address],
47019
- bankMetadataMap
47957
+ params.marginfiAccount,
47958
+ params.bankMap,
47959
+ [depositOpts.depositBank.address],
47960
+ params.bankMetadataMap
47020
47961
  );
47021
47962
  const kaminoRefreshIxs = makeRefreshKaminoBanksIxs(
47022
47963
  marginfiAccount,
47023
47964
  bankMap,
47024
- [withdrawOpts.withdrawBank.address, repayOpts.repayBank.address],
47965
+ [borrowOpts.borrowBank.address, depositOpts.depositBank.address],
47025
47966
  bankMetadataMap
47026
47967
  );
47027
- const { flashloanTx, setupInstructions, swapQuote, amountToRepay, withdrawIxs, repayIxs } = await buildRepayWithCollatFlashloanTx({
47968
+ const { flashloanTx, setupInstructions, swapQuote, amountToDeposit, depositIxs, borrowIxs } = await buildLoopFlashloanTx({
47028
47969
  ...params,
47029
47970
  blockhash
47030
47971
  });
@@ -47034,7 +47975,7 @@ async function makeRepayWithCollatTx(params) {
47034
47975
  }
47035
47976
  if (ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
47036
47977
  const mintKey = ix.keys[3]?.pubkey;
47037
- if (mintKey?.equals(withdrawOpts.withdrawBank.mint) || mintKey?.equals(repayOpts.repayBank.mint)) {
47978
+ if (mintKey?.equals(depositOpts.depositBank.mint) || mintKey?.equals(borrowOpts.borrowBank.mint)) {
47038
47979
  return false;
47039
47980
  }
47040
47981
  }
@@ -47046,18 +47987,24 @@ async function makeRepayWithCollatTx(params) {
47046
47987
  bankMap,
47047
47988
  oraclePrices,
47048
47989
  assetShareValueMultiplierByBank: params.assetShareValueMultiplierByBank,
47049
- instructions: [...withdrawIxs.instructions, ...repayIxs.instructions],
47990
+ instructions: [...borrowIxs.instructions, ...depositIxs.instructions],
47050
47991
  program,
47051
47992
  connection,
47052
47993
  crossbarUrl
47053
47994
  });
47054
47995
  let additionalTxs = [];
47055
- if (setupIxs.length > 0 || kaminoRefreshIxs.instructions.length > 0 || updateDriftMarketIxs.instructions.length > 0 || updateJuplendMarketIxs.instructions.length > 0) {
47996
+ if (depositOpts.depositBank.mint.equals(NATIVE_MINT) && depositOpts.inputDepositAmount) {
47997
+ setupIxs.push(
47998
+ ...makeWrapSolIxs(marginfiAccount.authority, new BigNumber3.BigNumber(depositOpts.inputDepositAmount))
47999
+ );
48000
+ }
48001
+ if (setupIxs.length > 0 || additionalIxs.length > 0 || kaminoRefreshIxs.instructions.length > 0 || updateDriftMarketIxs.instructions.length > 0 || updateJupLendRateIxs.instructions.length > 0) {
47056
48002
  const ixs = [
48003
+ ...additionalIxs,
47057
48004
  ...setupIxs,
47058
48005
  ...kaminoRefreshIxs.instructions,
47059
48006
  ...updateDriftMarketIxs.instructions,
47060
- ...updateJuplendMarketIxs.instructions
48007
+ ...updateJupLendRateIxs.instructions
47061
48008
  ];
47062
48009
  const txs = splitInstructionsToFitTransactions([], ixs, {
47063
48010
  blockhash,
@@ -47087,110 +48034,121 @@ async function makeRepayWithCollatTx(params) {
47087
48034
  );
47088
48035
  }
47089
48036
  const transactions = [...additionalTxs, flashloanTx];
47090
- return { transactions, swapQuote, amountToRepay };
48037
+ return {
48038
+ transactions,
48039
+ actionTxIndex: transactions.length - 1,
48040
+ quoteResponse: swapQuote
48041
+ };
47091
48042
  }
47092
- async function buildRepayWithCollatFlashloanTx({
48043
+ async function buildLoopFlashloanTx({
47093
48044
  program,
47094
48045
  marginfiAccount,
47095
48046
  bankMap,
47096
- withdrawOpts,
47097
- repayOpts,
48047
+ borrowOpts,
48048
+ depositOpts,
47098
48049
  bankMetadataMap,
47099
- assetShareValueMultiplierByBank,
47100
48050
  addressLookupTableAccounts,
47101
48051
  connection,
47102
48052
  swapOpts,
47103
48053
  overrideInferAccounts,
47104
48054
  blockhash
47105
48055
  }) {
47106
- const swapResult = [];
47107
48056
  const cuRequestIxs = [
47108
48057
  web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
47109
48058
  web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
47110
48059
  ];
47111
- if (repayOpts.repayBank.mint.equals(withdrawOpts.withdrawBank.mint)) {
47112
- swapResult.push({
47113
- amountToRepay: withdrawOpts.withdrawAmount,
47114
- swapInstructions: [],
47115
- setupInstructions: [],
47116
- swapLookupTables: []
47117
- });
48060
+ let amountToDeposit;
48061
+ let swapInstructions = [];
48062
+ let setupInstructions = [];
48063
+ let swapLookupTables = [];
48064
+ let swapQuote;
48065
+ let sizeConstraintUsed = 0;
48066
+ if (depositOpts.depositBank.mint.equals(borrowOpts.borrowBank.mint)) {
48067
+ amountToDeposit = borrowOpts.borrowAmount + (depositOpts.loopMode === "DEPOSIT" ? depositOpts.inputDepositAmount : 0);
47118
48068
  } else {
47119
48069
  const destinationTokenAccount = getAssociatedTokenAddressSync(
47120
- new web3_js.PublicKey(repayOpts.repayBank.mint),
48070
+ new web3_js.PublicKey(depositOpts.depositBank.mint),
47121
48071
  marginfiAccount.authority,
47122
48072
  true,
47123
- repayOpts.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
48073
+ depositOpts.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
47124
48074
  );
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
48075
+ const swapConstraints = await computeFlashloanSwapConstraints({
48076
+ program,
48077
+ marginfiAccount,
48078
+ bankMap,
48079
+ bankMetadataMap,
48080
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
48081
+ primaryIx: {
48082
+ type: "borrow",
48083
+ bank: borrowOpts.borrowBank,
48084
+ tokenProgram: borrowOpts.tokenProgram
48085
+ },
48086
+ secondaryIx: {
48087
+ type: "deposit",
48088
+ bank: depositOpts.depositBank,
48089
+ tokenProgram: depositOpts.tokenProgram
47138
48090
  },
48091
+ overrideInferAccounts
48092
+ });
48093
+ const swapResponse = await getSwapIxsForFlashloan({
48094
+ inputMint: borrowOpts.borrowBank.mint.toBase58(),
48095
+ outputMint: depositOpts.depositBank.mint.toBase58(),
48096
+ amount: uiToNative(borrowOpts.borrowAmount, borrowOpts.borrowBank.mintDecimals).toNumber(),
48097
+ swapMode: "ExactIn",
47139
48098
  authority: marginfiAccount.authority,
47140
48099
  connection,
47141
48100
  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
- });
48101
+ swapOpts,
48102
+ sizeConstraint: swapConstraints.sizeConstraint,
48103
+ maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
47159
48104
  });
48105
+ sizeConstraintUsed = swapConstraints.sizeConstraint;
48106
+ const outAmountThreshold = nativeToUi(
48107
+ swapResponse.quoteResponse.otherAmountThreshold,
48108
+ depositOpts.depositBank.mintDecimals
48109
+ );
48110
+ amountToDeposit = outAmountThreshold + (depositOpts.loopMode === "DEPOSIT" ? depositOpts.inputDepositAmount : 0);
48111
+ swapInstructions = swapResponse.swapInstructions;
48112
+ setupInstructions = swapResponse.setupInstructions;
48113
+ swapLookupTables = swapResponse.addressLookupTableAddresses;
48114
+ swapQuote = swapResponse.quoteResponse;
47160
48115
  }
47161
- let withdrawIxs;
47162
- switch (withdrawOpts.withdrawBank.config.assetTag) {
48116
+ const borrowIxs = await makeBorrowIx3({
48117
+ program,
48118
+ bank: borrowOpts.borrowBank,
48119
+ bankMap,
48120
+ tokenProgram: borrowOpts.tokenProgram,
48121
+ amount: borrowOpts.borrowAmount,
48122
+ marginfiAccount,
48123
+ authority: marginfiAccount.authority,
48124
+ isSync: false,
48125
+ opts: {
48126
+ createAtas: false,
48127
+ wrapAndUnwrapSol: false,
48128
+ overrideInferAccounts
48129
+ }
48130
+ });
48131
+ let depositIxs;
48132
+ switch (depositOpts.depositBank.config.assetTag) {
47163
48133
  case 3 /* KAMINO */: {
47164
- const reserve = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.kaminoStates?.reserveState;
48134
+ const reserve = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.kaminoStates?.reserveState;
47165
48135
  if (!reserve) {
47166
48136
  throw TransactionBuildingError.kaminoReserveNotFound(
47167
- withdrawOpts.withdrawBank.address.toBase58(),
47168
- withdrawOpts.withdrawBank.mint.toBase58(),
47169
- withdrawOpts.withdrawBank.tokenSymbol
48137
+ depositOpts.depositBank.address.toBase58(),
48138
+ depositOpts.depositBank.mint.toBase58(),
48139
+ depositOpts.depositBank.tokenSymbol
47170
48140
  );
47171
48141
  }
47172
- const multiplier = assetShareValueMultiplierByBank.get(withdrawOpts.withdrawBank.address.toBase58()) ?? new BigNumber3.BigNumber(1);
47173
- const adjustedAmount = new BigNumber3.BigNumber(withdrawOpts.withdrawAmount).div(multiplier).times(1.0001).toNumber();
47174
- withdrawIxs = await makeKaminoWithdrawIx3({
48142
+ depositIxs = await makeKaminoDepositIx3({
47175
48143
  program,
47176
- bank: withdrawOpts.withdrawBank,
47177
- bankMap,
47178
- tokenProgram: withdrawOpts.tokenProgram,
47179
- cTokenAmount: adjustedAmount,
47180
- marginfiAccount,
48144
+ bank: depositOpts.depositBank,
48145
+ tokenProgram: depositOpts.tokenProgram,
48146
+ amount: amountToDeposit,
48147
+ accountAddress: marginfiAccount.address,
47181
48148
  authority: marginfiAccount.authority,
48149
+ group: marginfiAccount.group,
47182
48150
  reserve,
47183
- withdrawAll: isWholePosition(
47184
- {
47185
- amount: withdrawOpts.totalPositionAmount,
47186
- isLending: true
47187
- },
47188
- withdrawOpts.withdrawAmount,
47189
- withdrawOpts.withdrawBank.mintDecimals
47190
- ),
47191
- isSync: false,
47192
48151
  opts: {
47193
- createAtas: false,
47194
48152
  wrapAndUnwrapSol: false,
47195
48153
  overrideInferAccounts
47196
48154
  }
@@ -47198,35 +48156,27 @@ async function buildRepayWithCollatFlashloanTx({
47198
48156
  break;
47199
48157
  }
47200
48158
  case 4 /* DRIFT */: {
47201
- const driftState = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.driftStates;
48159
+ const driftState = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.driftStates;
47202
48160
  if (!driftState) {
47203
48161
  throw TransactionBuildingError.driftStateNotFound(
47204
- withdrawOpts.withdrawBank.address.toBase58(),
47205
- withdrawOpts.withdrawBank.mint.toBase58(),
47206
- withdrawOpts.withdrawBank.tokenSymbol
48162
+ depositOpts.depositBank.address.toBase58(),
48163
+ depositOpts.depositBank.mint.toBase58(),
48164
+ depositOpts.depositBank.tokenSymbol
47207
48165
  );
47208
48166
  }
47209
- withdrawIxs = await makeDriftWithdrawIx3({
48167
+ const driftMarketIndex = driftState.spotMarketState.marketIndex;
48168
+ const driftOracle = driftState.spotMarketState.oracle;
48169
+ depositIxs = await makeDriftDepositIx3({
47210
48170
  program,
47211
- bank: withdrawOpts.withdrawBank,
47212
- bankMap,
47213
- tokenProgram: withdrawOpts.tokenProgram,
47214
- amount: withdrawOpts.withdrawAmount,
47215
- marginfiAccount,
48171
+ bank: depositOpts.depositBank,
48172
+ tokenProgram: depositOpts.tokenProgram,
48173
+ amount: amountToDeposit,
48174
+ accountAddress: marginfiAccount.address,
47216
48175
  authority: marginfiAccount.authority,
47217
- driftSpotMarket: driftState.spotMarketState,
47218
- userRewards: driftState.userRewards,
47219
- withdrawAll: isWholePosition(
47220
- {
47221
- amount: withdrawOpts.totalPositionAmount,
47222
- isLending: true
47223
- },
47224
- withdrawOpts.withdrawAmount,
47225
- withdrawOpts.withdrawBank.mintDecimals
47226
- ),
47227
- isSync: false,
48176
+ group: marginfiAccount.group,
48177
+ driftMarketIndex,
48178
+ driftOracle,
47228
48179
  opts: {
47229
- createAtas: false,
47230
48180
  wrapAndUnwrapSol: false,
47231
48181
  overrideInferAccounts
47232
48182
  }
@@ -47234,33 +48184,15 @@ async function buildRepayWithCollatFlashloanTx({
47234
48184
  break;
47235
48185
  }
47236
48186
  case 6 /* JUPLEND */: {
47237
- const jupLendState = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.jupLendStates;
47238
- if (!jupLendState) {
47239
- throw TransactionBuildingError.jupLendStateNotFound(
47240
- withdrawOpts.withdrawBank.address.toBase58(),
47241
- withdrawOpts.withdrawBank.mint.toBase58(),
47242
- withdrawOpts.withdrawBank.tokenSymbol
47243
- );
47244
- }
47245
- withdrawIxs = await makeJuplendWithdrawIx2({
48187
+ depositIxs = await makeJuplendDepositIx2({
47246
48188
  program,
47247
- bank: withdrawOpts.withdrawBank,
47248
- bankMap,
47249
- tokenProgram: withdrawOpts.tokenProgram,
47250
- amount: withdrawOpts.withdrawAmount,
47251
- marginfiAccount,
48189
+ bank: depositOpts.depositBank,
48190
+ tokenProgram: depositOpts.tokenProgram,
48191
+ amount: amountToDeposit,
48192
+ accountAddress: marginfiAccount.address,
47252
48193
  authority: marginfiAccount.authority,
47253
- jupLendingState: jupLendState.jupLendingState,
47254
- withdrawAll: isWholePosition(
47255
- {
47256
- amount: withdrawOpts.totalPositionAmount,
47257
- isLending: true
47258
- },
47259
- withdrawOpts.withdrawAmount,
47260
- withdrawOpts.withdrawBank.mintDecimals
47261
- ),
48194
+ group: marginfiAccount.group,
47262
48195
  opts: {
47263
- createAtas: false,
47264
48196
  wrapAndUnwrapSol: false,
47265
48197
  overrideInferAccounts
47266
48198
  }
@@ -47268,25 +48200,15 @@ async function buildRepayWithCollatFlashloanTx({
47268
48200
  break;
47269
48201
  }
47270
48202
  default: {
47271
- withdrawIxs = await makeWithdrawIx3({
48203
+ depositIxs = await makeDepositIx3({
47272
48204
  program,
47273
- bank: withdrawOpts.withdrawBank,
47274
- bankMap,
47275
- tokenProgram: withdrawOpts.tokenProgram,
47276
- amount: withdrawOpts.withdrawAmount,
47277
- marginfiAccount,
48205
+ bank: depositOpts.depositBank,
48206
+ tokenProgram: depositOpts.tokenProgram,
48207
+ amount: amountToDeposit,
48208
+ accountAddress: marginfiAccount.address,
47278
48209
  authority: marginfiAccount.authority,
47279
- withdrawAll: isWholePosition(
47280
- {
47281
- amount: withdrawOpts.totalPositionAmount,
47282
- isLending: true
47283
- },
47284
- withdrawOpts.withdrawAmount,
47285
- withdrawOpts.withdrawBank.mintDecimals
47286
- ),
47287
- isSync: false,
48210
+ group: marginfiAccount.group,
47288
48211
  opts: {
47289
- createAtas: false,
47290
48212
  wrapAndUnwrapSol: false,
47291
48213
  overrideInferAccounts
47292
48214
  }
@@ -47294,68 +48216,48 @@ async function buildRepayWithCollatFlashloanTx({
47294
48216
  break;
47295
48217
  }
47296
48218
  }
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
48219
+ const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
48220
+ const allNonFlIxs = [
48221
+ ...cuRequestIxs,
48222
+ ...borrowIxs.instructions,
48223
+ ...swapInstructions,
48224
+ ...depositIxs.instructions
48225
+ ];
48226
+ if (swapInstructions.length > 0) {
48227
+ compileFlashloanPrecheck({
48228
+ allIxs: allNonFlIxs,
48229
+ payer: marginfiAccount.authority,
48230
+ luts,
48231
+ sizeConstraint: sizeConstraintUsed,
48232
+ swapIxCount: swapInstructions.length,
48233
+ swapLutCount: swapLookupTables.length
47337
48234
  });
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
- };
47356
- }
47357
48235
  }
47358
- throw new Error("Failed to build repay with collateral flashloan tx");
48236
+ const flashloanTx = await makeFlashLoanTx({
48237
+ program,
48238
+ marginfiAccount,
48239
+ bankMap,
48240
+ addressLookupTableAccounts: luts,
48241
+ blockhash,
48242
+ ixs: allNonFlIxs
48243
+ });
48244
+ const txSize = getTxSize(flashloanTx);
48245
+ const totalKeys = getTotalAccountKeys(flashloanTx);
48246
+ if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
48247
+ throw TransactionBuildingError.swapSizeExceededLoop(
48248
+ txSize,
48249
+ totalKeys,
48250
+ swapOpts.swapConfig?.provider
48251
+ );
48252
+ }
48253
+ return {
48254
+ flashloanTx,
48255
+ setupInstructions,
48256
+ swapQuote,
48257
+ borrowIxs,
48258
+ depositIxs,
48259
+ amountToDeposit
48260
+ };
47359
48261
  }
47360
48262
 
47361
48263
  // src/services/account/actions/emissions.ts
@@ -47509,11 +48411,16 @@ async function buildSwapCollateralFlashloanTx({
47509
48411
  actualWithdrawAmount,
47510
48412
  withdrawBank.mintDecimals
47511
48413
  );
47512
- const swapResult = [];
47513
48414
  const cuRequestIxs = [
47514
48415
  web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
47515
48416
  web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
47516
48417
  ];
48418
+ let amountToDeposit;
48419
+ let swapInstructions = [];
48420
+ let setupInstructions = [];
48421
+ let swapLookupTables = [];
48422
+ let swapQuote;
48423
+ let sizeConstraintUsed = 0;
47517
48424
  let withdrawIxs;
47518
48425
  switch (withdrawOpts.withdrawBank.config.assetTag) {
47519
48426
  case 3 /* KAMINO */: {
@@ -47623,12 +48530,7 @@ async function buildSwapCollateralFlashloanTx({
47623
48530
  }
47624
48531
  }
47625
48532
  if (depositBank.mint.equals(withdrawBank.mint)) {
47626
- swapResult.push({
47627
- amountToDeposit: actualWithdrawAmount,
47628
- swapInstructions: [],
47629
- setupInstructions: [],
47630
- swapLookupTables: []
47631
- });
48533
+ amountToDeposit = actualWithdrawAmount;
47632
48534
  } else {
47633
48535
  const destinationTokenAccount = getAssociatedTokenAddressSync(
47634
48536
  depositBank.mint,
@@ -47636,175 +48538,168 @@ async function buildSwapCollateralFlashloanTx({
47636
48538
  true,
47637
48539
  depositTokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
47638
48540
  );
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
- },
48541
+ const swapConstraints = await computeFlashloanSwapConstraints({
48542
+ program,
48543
+ marginfiAccount,
48544
+ bankMap,
48545
+ bankMetadataMap,
48546
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
48547
+ primaryIx: { type: "withdraw", bank: withdrawBank, tokenProgram: withdrawTokenProgram },
48548
+ secondaryIx: { type: "deposit", bank: depositBank, tokenProgram: depositTokenProgram },
48549
+ overrideInferAccounts
48550
+ });
48551
+ const swapResponses = await getSwapIxsForFlashloan({
48552
+ inputMint: withdrawBank.mint.toBase58(),
48553
+ outputMint: depositBank.mint.toBase58(),
48554
+ amount: uiToNative(actualWithdrawAmount, withdrawBank.mintDecimals).toNumber(),
48555
+ swapMode: "ExactIn",
47650
48556
  authority: marginfiAccount.authority,
47651
48557
  connection,
47652
48558
  destinationTokenAccount,
47653
- configParams: swapOpts.jupiterOptions?.configParams
48559
+ swapOpts,
48560
+ sizeConstraint: swapConstraints.sizeConstraint,
48561
+ maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
47654
48562
  });
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
- });
47667
- });
47668
- }
47669
- if (swapResult.length === 0) {
47670
- throw new Error(
47671
- `No swap routes found for ${withdrawBank.mint.toBase58()} -> ${depositBank.mint.toBase58()}`
48563
+ sizeConstraintUsed = swapConstraints.sizeConstraint;
48564
+ amountToDeposit = nativeToUi(
48565
+ swapResponses.quoteResponse.otherAmountThreshold,
48566
+ depositBank.mintDecimals
47672
48567
  );
48568
+ swapInstructions = swapResponses.swapInstructions;
48569
+ setupInstructions = swapResponses.setupInstructions;
48570
+ swapLookupTables = swapResponses.addressLookupTableAddresses;
48571
+ swapQuote = swapResponses.quoteResponse;
47673
48572
  }
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;
48573
+ let depositIxs;
48574
+ switch (depositBank.config.assetTag) {
48575
+ case 3 /* KAMINO */: {
48576
+ const reserve = bankMetadataMap[depositBank.address.toBase58()]?.kaminoStates?.reserveState;
48577
+ if (!reserve) {
48578
+ throw TransactionBuildingError.kaminoReserveNotFound(
48579
+ depositBank.address.toBase58(),
48580
+ depositBank.mint.toBase58(),
48581
+ depositBank.tokenSymbol
48582
+ );
47708
48583
  }
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
- );
48584
+ depositIxs = await makeKaminoDepositIx3({
48585
+ program,
48586
+ bank: depositBank,
48587
+ tokenProgram: depositTokenProgram,
48588
+ amount: amountToDeposit,
48589
+ accountAddress: marginfiAccount.address,
48590
+ authority: marginfiAccount.authority,
48591
+ group: marginfiAccount.group,
48592
+ reserve,
48593
+ opts: {
48594
+ wrapAndUnwrapSol: false,
48595
+ overrideInferAccounts
47717
48596
  }
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
- }
48597
+ });
48598
+ break;
47769
48599
  }
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;
48600
+ case 4 /* DRIFT */: {
48601
+ const driftState = bankMetadataMap[depositBank.address.toBase58()]?.driftStates;
48602
+ if (!driftState) {
48603
+ throw TransactionBuildingError.driftStateNotFound(
48604
+ depositBank.address.toBase58(),
48605
+ depositBank.mint.toBase58(),
48606
+ depositBank.tokenSymbol
48607
+ );
47796
48608
  }
47797
- } else {
47798
- return {
47799
- flashloanTx,
47800
- setupInstructions,
47801
- swapQuote: quoteResponse,
47802
- withdrawIxs,
47803
- depositIxs
47804
- };
48609
+ const driftMarketIndex = driftState.spotMarketState.marketIndex;
48610
+ const driftOracle = driftState.spotMarketState.oracle;
48611
+ depositIxs = await makeDriftDepositIx3({
48612
+ program,
48613
+ bank: depositBank,
48614
+ tokenProgram: depositTokenProgram,
48615
+ amount: amountToDeposit,
48616
+ accountAddress: marginfiAccount.address,
48617
+ authority: marginfiAccount.authority,
48618
+ group: marginfiAccount.group,
48619
+ driftMarketIndex,
48620
+ driftOracle,
48621
+ opts: {
48622
+ wrapAndUnwrapSol: false,
48623
+ overrideInferAccounts
48624
+ }
48625
+ });
48626
+ break;
48627
+ }
48628
+ case 6 /* JUPLEND */: {
48629
+ depositIxs = await makeJuplendDepositIx2({
48630
+ program,
48631
+ bank: depositBank,
48632
+ tokenProgram: depositTokenProgram,
48633
+ amount: amountToDeposit,
48634
+ accountAddress: marginfiAccount.address,
48635
+ authority: marginfiAccount.authority,
48636
+ group: marginfiAccount.group,
48637
+ opts: {
48638
+ wrapAndUnwrapSol: false,
48639
+ overrideInferAccounts
48640
+ }
48641
+ });
48642
+ break;
48643
+ }
48644
+ default: {
48645
+ depositIxs = await makeDepositIx3({
48646
+ program,
48647
+ bank: depositBank,
48648
+ tokenProgram: depositTokenProgram,
48649
+ amount: amountToDeposit,
48650
+ accountAddress: marginfiAccount.address,
48651
+ authority: marginfiAccount.authority,
48652
+ group: marginfiAccount.group,
48653
+ opts: {
48654
+ wrapAndUnwrapSol: false,
48655
+ overrideInferAccounts
48656
+ }
48657
+ });
48658
+ break;
47805
48659
  }
47806
48660
  }
47807
- throw new Error("Failed to build swap collateral flashloan tx");
48661
+ const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
48662
+ const allNonFlIxs = [
48663
+ ...cuRequestIxs,
48664
+ ...withdrawIxs.instructions,
48665
+ ...swapInstructions,
48666
+ ...depositIxs.instructions
48667
+ ];
48668
+ if (swapInstructions.length > 0) {
48669
+ compileFlashloanPrecheck({
48670
+ allIxs: allNonFlIxs,
48671
+ payer: marginfiAccount.authority,
48672
+ luts,
48673
+ sizeConstraint: sizeConstraintUsed,
48674
+ swapIxCount: swapInstructions.length,
48675
+ swapLutCount: swapLookupTables.length
48676
+ });
48677
+ }
48678
+ const flashloanTx = await makeFlashLoanTx({
48679
+ program,
48680
+ marginfiAccount,
48681
+ bankMap,
48682
+ addressLookupTableAccounts: luts,
48683
+ blockhash,
48684
+ ixs: allNonFlIxs,
48685
+ isSync: true
48686
+ });
48687
+ const txSize = getTxSize(flashloanTx);
48688
+ const totalKeys = getTotalAccountKeys(flashloanTx);
48689
+ if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
48690
+ throw TransactionBuildingError.swapSizeExceededLoop(
48691
+ txSize,
48692
+ totalKeys,
48693
+ swapOpts.swapConfig?.provider
48694
+ );
48695
+ }
48696
+ return {
48697
+ flashloanTx,
48698
+ setupInstructions,
48699
+ swapQuote,
48700
+ withdrawIxs,
48701
+ depositIxs
48702
+ };
47808
48703
  }
47809
48704
  async function makeSwapDebtTx(params) {
47810
48705
  const {
@@ -47942,7 +48837,6 @@ async function buildSwapDebtFlashloanTx({
47942
48837
  throw new Error("repayAmount must be greater than 0");
47943
48838
  }
47944
48839
  const actualRepayAmount = Math.min(repayAmount ?? totalPositionAmount, totalPositionAmount);
47945
- const swapResult = [];
47946
48840
  const cuRequestIxs = [
47947
48841
  web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
47948
48842
  web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
@@ -47953,140 +48847,120 @@ async function buildSwapDebtFlashloanTx({
47953
48847
  true,
47954
48848
  repayTokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
47955
48849
  );
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({
48850
+ const { otherAmountThreshold } = await getExactOutEstimate({
47958
48851
  inputMint: borrowBank.mint.toBase58(),
47959
48852
  outputMint: repayBank.mint.toBase58(),
47960
48853
  amount: uiToNative(actualRepayAmount, repayBank.mintDecimals).toNumber(),
47961
- swapMode: "ExactOut",
47962
- dynamicSlippage: swapOpts.jupiterOptions ? swapOpts.jupiterOptions.slippageMode === "DYNAMIC" : true,
47963
- slippageBps: swapOpts.jupiterOptions?.slippageBps
48854
+ swapOpts,
48855
+ connection
47964
48856
  });
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
- },
48857
+ const estimatedBorrowAmount = nativeToUi(otherAmountThreshold, borrowBank.mintDecimals);
48858
+ const swapConstraints = await computeFlashloanSwapConstraints({
48859
+ program,
48860
+ marginfiAccount,
48861
+ bankMap,
48862
+ bankMetadataMap,
48863
+ addressLookupTableAccounts: addressLookupTableAccounts ?? [],
48864
+ primaryIx: { type: "borrow", bank: borrowBank, tokenProgram: borrowTokenProgram },
48865
+ secondaryIx: { type: "repay", bank: repayBank, tokenProgram: repayTokenProgram },
48866
+ overrideInferAccounts
48867
+ });
48868
+ const swapResponses = await getSwapIxsForFlashloan({
48869
+ inputMint: borrowBank.mint.toBase58(),
48870
+ outputMint: repayBank.mint.toBase58(),
48871
+ amount: uiToNative(estimatedBorrowAmount, borrowBank.mintDecimals).toNumber(),
48872
+ swapMode: "ExactIn",
47980
48873
  authority: marginfiAccount.authority,
47981
48874
  connection,
47982
48875
  destinationTokenAccount,
47983
- configParams: swapOpts.jupiterOptions?.configParams
48876
+ swapOpts,
48877
+ sizeConstraint: swapConstraints.sizeConstraint,
48878
+ maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
47984
48879
  });
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
- });
48880
+ const { quoteResponse } = swapResponses;
48881
+ const outAmount = nativeToUi(quoteResponse.outAmount, repayBank.mintDecimals);
48882
+ const outAmountThreshold = nativeToUi(quoteResponse.otherAmountThreshold, repayBank.mintDecimals);
48883
+ const amountToRepay = outAmount > totalPositionAmount ? totalPositionAmount : outAmountThreshold;
48884
+ const borrowAmount = nativeToUi(quoteResponse.inAmount, borrowBank.mintDecimals);
48885
+ const borrowIxs = await makeBorrowIx3({
48886
+ program,
48887
+ bank: borrowBank,
48888
+ bankMap,
48889
+ tokenProgram: borrowTokenProgram,
48890
+ amount: borrowAmount,
48891
+ marginfiAccount,
48892
+ authority: marginfiAccount.authority,
48893
+ isSync: true,
48894
+ opts: {
48895
+ createAtas: false,
48896
+ wrapAndUnwrapSol: false,
48897
+ overrideInferAccounts
48898
+ }
48001
48899
  });
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 {
48900
+ const repayIxs = await makeRepayIx3({
48901
+ program,
48902
+ bank: repayBank,
48903
+ tokenProgram: repayTokenProgram,
48904
+ amount: amountToRepay,
48905
+ accountAddress: marginfiAccount.address,
48906
+ authority: marginfiAccount.authority,
48907
+ repayAll: isWholePosition(
48908
+ {
48909
+ amount: totalPositionAmount,
48910
+ isLending: false
48911
+ },
48009
48912
  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
- };
48913
+ repayBank.mintDecimals
48914
+ ),
48915
+ isSync: true,
48916
+ opts: {
48917
+ wrapAndUnwrapSol: false,
48918
+ overrideInferAccounts
48087
48919
  }
48920
+ });
48921
+ const luts = [
48922
+ ...addressLookupTableAccounts ?? [],
48923
+ ...swapResponses.addressLookupTableAddresses
48924
+ ];
48925
+ const allNonFlIxs = [
48926
+ ...cuRequestIxs,
48927
+ ...borrowIxs.instructions,
48928
+ ...swapResponses.swapInstructions,
48929
+ ...repayIxs.instructions
48930
+ ];
48931
+ compileFlashloanPrecheck({
48932
+ allIxs: allNonFlIxs,
48933
+ payer: marginfiAccount.authority,
48934
+ luts,
48935
+ sizeConstraint: swapConstraints.sizeConstraint,
48936
+ swapIxCount: swapResponses.swapInstructions.length,
48937
+ swapLutCount: swapResponses.addressLookupTableAddresses.length
48938
+ });
48939
+ const flashloanTx = await makeFlashLoanTx({
48940
+ program,
48941
+ marginfiAccount,
48942
+ bankMap,
48943
+ addressLookupTableAccounts: luts,
48944
+ blockhash,
48945
+ ixs: allNonFlIxs,
48946
+ isSync: true
48947
+ });
48948
+ const txSize = getTxSize(flashloanTx);
48949
+ const totalKeys = getTotalAccountKeys(flashloanTx);
48950
+ if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
48951
+ throw TransactionBuildingError.swapSizeExceededLoop(
48952
+ txSize,
48953
+ totalKeys,
48954
+ swapOpts.swapConfig?.provider
48955
+ );
48088
48956
  }
48089
- throw new Error("Failed to build swap debt flashloan tx");
48957
+ return {
48958
+ flashloanTx,
48959
+ setupInstructions: swapResponses.setupInstructions,
48960
+ swapQuote: quoteResponse,
48961
+ borrowIxs,
48962
+ repayIxs
48963
+ };
48090
48964
  }
48091
48965
  var SYSVAR_CLOCK_ID2 = new web3_js.PublicKey("SysvarC1ock11111111111111111111111111111111");
48092
48966
  async function makeMintStakedLstIx(params) {
@@ -48885,17 +49759,528 @@ function computeMaxWithdrawForBank(params) {
48885
49759
  const maxWithdraw = initUntiedCollateralForBank.div(initWeightedPrice);
48886
49760
  return maxWithdraw;
48887
49761
  }
49762
+ var TITAN_FEE_WALLET = new web3_js.PublicKey("6ryqDDCwKFZfSiHQrYRkjTEarbsLjg9TmuFg1RJorBk3");
49763
+ var getTitanFeeAccount = (mint) => {
49764
+ return getAssociatedTokenAddressSync(mint, TITAN_FEE_WALLET, true);
49765
+ };
49766
+ var checkTitanFeeAccount = async (connection, mint) => {
49767
+ const feeAccount = getTitanFeeAccount(mint);
49768
+ const hasFeeAccount = !!await connection.getAccountInfo(feeAccount);
49769
+ return { feeAccount, hasFeeAccount, feeWallet: TITAN_FEE_WALLET };
49770
+ };
49771
+ function deserializeTitanInstruction(ix) {
49772
+ return new web3_js.TransactionInstruction({
49773
+ programId: new web3_js.PublicKey(ix.p),
49774
+ keys: ix.a.map((account) => ({
49775
+ pubkey: new web3_js.PublicKey(account.p),
49776
+ isSigner: account.s,
49777
+ isWritable: account.w
49778
+ })),
49779
+ data: Buffer.from(ix.d)
49780
+ });
49781
+ }
49782
+ var getTitanSwapIxsForFlashloan = async ({
49783
+ quoteParams,
49784
+ authority,
49785
+ connection,
49786
+ destinationTokenAccount,
49787
+ apiConfig
49788
+ }) => {
49789
+ const basePath = apiConfig?.basePath ?? "";
49790
+ const feeMint = new web3_js.PublicKey(
49791
+ quoteParams.swapMode === "ExactIn" ? quoteParams.outputMint : quoteParams.inputMint
49792
+ );
49793
+ const { feeAccount, hasFeeAccount } = await checkTitanFeeAccount(connection, feeMint);
49794
+ let finalQuoteParams = quoteParams;
49795
+ if (!hasFeeAccount) {
49796
+ console.warn("Warning: Titan fee account ATA does not exist, disabling platform fee");
49797
+ finalQuoteParams = {
49798
+ ...quoteParams,
49799
+ platformFeeBps: void 0
49800
+ };
49801
+ }
49802
+ if (basePath.startsWith("wss://") || basePath.startsWith("ws://")) {
49803
+ return getTitanSwapIxsViaWebSocket(
49804
+ { quoteParams: finalQuoteParams, authority, connection, destinationTokenAccount, apiConfig },
49805
+ hasFeeAccount ? feeAccount : void 0
49806
+ );
49807
+ } else {
49808
+ return getTitanSwapIxsViaHttpProxy(
49809
+ { quoteParams: finalQuoteParams, authority, connection, destinationTokenAccount, apiConfig },
49810
+ hasFeeAccount ? feeAccount : void 0
49811
+ );
49812
+ }
49813
+ };
49814
+ async function getTitanSwapIxsViaWebSocket(params, feeAccount) {
49815
+ const { quoteParams, authority, connection, destinationTokenAccount, apiConfig } = params;
49816
+ const {
49817
+ inputMint,
49818
+ outputMint,
49819
+ amount,
49820
+ swapMode,
49821
+ slippageBps,
49822
+ platformFeeBps,
49823
+ directRoutesOnly,
49824
+ sizeConstraint,
49825
+ maxSwapAccounts,
49826
+ maxSwapTotalAccounts
49827
+ } = quoteParams;
49828
+ const wsUrl = apiConfig?.basePath;
49829
+ if (!wsUrl) {
49830
+ throw new Error("Titan WebSocket URL is required (apiConfig.basePath)");
49831
+ }
49832
+ const txParams = {
49833
+ userPublicKey: authority.toBytes(),
49834
+ outputAccount: destinationTokenAccount.toBytes()
49835
+ };
49836
+ if (feeAccount && platformFeeBps) {
49837
+ txParams.feeBps = platformFeeBps;
49838
+ txParams.feeAccount = feeAccount.toBytes();
49839
+ }
49840
+ const client = await V1Client.connect(wsUrl);
49841
+ try {
49842
+ const { stream } = await client.newSwapQuoteStream({
49843
+ swap: {
49844
+ inputMint: new web3_js.PublicKey(inputMint).toBytes(),
49845
+ outputMint: new web3_js.PublicKey(outputMint).toBytes(),
49846
+ amount,
49847
+ swapMode,
49848
+ slippageBps,
49849
+ onlyDirectRoutes: directRoutesOnly,
49850
+ addSizeConstraint: sizeConstraint !== void 0,
49851
+ sizeConstraint,
49852
+ accountsLimitWritable: maxSwapAccounts,
49853
+ accountsLimitTotal: maxSwapTotalAccounts
49854
+ },
49855
+ transaction: txParams,
49856
+ update: {
49857
+ num_quotes: 3
49858
+ }
49859
+ });
49860
+ const reader = stream.getReader();
49861
+ const { value: swapQuotes, done } = await reader.read();
49862
+ reader.releaseLock();
49863
+ if (done || !swapQuotes) {
49864
+ throw new Error("Titan swap quote stream ended without data");
49865
+ }
49866
+ const quotes = swapQuotes;
49867
+ const bestRoute = selectBestRoute(quotes.quotes, swapMode);
49868
+ if (!bestRoute) {
49869
+ throw new Error(`No Titan swap routes found for ${inputMint} -> ${outputMint}`);
49870
+ }
49871
+ const swapInstructions = bestRoute.instructions.map(deserializeTitanInstruction);
49872
+ const lutPubkeys = bestRoute.addressLookupTables.map((lutBytes) => new web3_js.PublicKey(lutBytes));
49873
+ const addressLookupTableAddresses = await resolveLookupTables(connection, lutPubkeys);
49874
+ const quoteResponse = {
49875
+ ...buildSwapQuoteResult(bestRoute, swapMode),
49876
+ provider: "TITAN" /* TITAN */
49877
+ };
49878
+ return {
49879
+ swapInstructions,
49880
+ setupInstructions: [],
49881
+ addressLookupTableAddresses,
49882
+ quoteResponse
49883
+ };
49884
+ } finally {
49885
+ if (!client.closed) {
49886
+ await client.close();
49887
+ }
49888
+ }
49889
+ }
49890
+ async function getTitanSwapIxsViaHttpProxy(params, feeAccount) {
49891
+ const { quoteParams, authority, connection, destinationTokenAccount, apiConfig } = params;
49892
+ const {
49893
+ inputMint,
49894
+ outputMint,
49895
+ amount,
49896
+ swapMode,
49897
+ slippageBps,
49898
+ platformFeeBps,
49899
+ directRoutesOnly,
49900
+ sizeConstraint,
49901
+ maxSwapAccounts,
49902
+ maxSwapTotalAccounts
49903
+ } = quoteParams;
49904
+ const basePath = apiConfig?.basePath;
49905
+ if (!basePath) {
49906
+ throw new Error("Titan proxy URL is required (apiConfig.basePath)");
49907
+ }
49908
+ const txBody = {
49909
+ userPublicKey: authority.toBase58(),
49910
+ outputAccount: destinationTokenAccount.toBase58()
49911
+ };
49912
+ if (feeAccount && platformFeeBps) {
49913
+ txBody.feeBps = platformFeeBps;
49914
+ txBody.feeAccount = feeAccount.toBase58();
49915
+ }
49916
+ const response = await fetch(`${basePath}/swap-quote`, {
49917
+ method: "POST",
49918
+ headers: {
49919
+ "Content-Type": "application/json",
49920
+ ...apiConfig?.headers ?? {}
49921
+ },
49922
+ body: JSON.stringify({
49923
+ swap: {
49924
+ inputMint,
49925
+ outputMint,
49926
+ amount,
49927
+ swapMode,
49928
+ slippageBps,
49929
+ onlyDirectRoutes: directRoutesOnly,
49930
+ addSizeConstraint: sizeConstraint !== void 0,
49931
+ sizeConstraint,
49932
+ accountsLimitWritable: maxSwapAccounts,
49933
+ accountsLimitTotal: maxSwapTotalAccounts
49934
+ },
49935
+ transaction: txBody
49936
+ })
49937
+ });
49938
+ if (!response.ok) {
49939
+ const errorData = await response.json().catch(() => ({}));
49940
+ throw new Error(
49941
+ `Titan proxy error (${response.status}): ${errorData.message ?? response.statusText}`
49942
+ );
49943
+ }
49944
+ const data = await response.json();
49945
+ const bestRoute = selectBestRoute(data.quotes, swapMode);
49946
+ if (!bestRoute) {
49947
+ throw new Error(`No Titan swap routes found for ${inputMint} -> ${outputMint}`);
49948
+ }
49949
+ const swapInstructions = bestRoute.instructions.map(deserializeSerializedInstruction);
49950
+ const lutPubkeys = bestRoute.addressLookupTables.map(
49951
+ (b64) => new web3_js.PublicKey(Buffer.from(b64, "base64"))
49952
+ );
49953
+ const addressLookupTableAddresses = await resolveLookupTables(connection, lutPubkeys);
49954
+ const quoteResponse = {
49955
+ ...buildSwapQuoteResult(bestRoute, swapMode),
49956
+ provider: "TITAN" /* TITAN */
49957
+ };
49958
+ return {
49959
+ swapInstructions,
49960
+ setupInstructions: [],
49961
+ addressLookupTableAddresses,
49962
+ quoteResponse
49963
+ };
49964
+ }
49965
+ var getTitanExactOutEstimate = async (params) => {
49966
+ const basePath = params.apiConfig?.basePath ?? "";
49967
+ if (basePath.startsWith("wss://") || basePath.startsWith("ws://")) {
49968
+ return getTitanExactOutViaWebSocket(params);
49969
+ } else {
49970
+ return getTitanExactOutViaHttpProxy(params);
49971
+ }
49972
+ };
49973
+ async function getTitanExactOutViaWebSocket(params) {
49974
+ const { inputMint, outputMint, amount, slippageBps, apiConfig } = params;
49975
+ const wsUrl = apiConfig?.basePath;
49976
+ if (!wsUrl) {
49977
+ throw new Error("Titan WebSocket URL is required (apiConfig.basePath)");
49978
+ }
49979
+ const client = await V1Client.connect(wsUrl);
49980
+ try {
49981
+ const { stream } = await client.newSwapQuoteStream({
49982
+ swap: {
49983
+ inputMint: new web3_js.PublicKey(inputMint).toBytes(),
49984
+ outputMint: new web3_js.PublicKey(outputMint).toBytes(),
49985
+ amount,
49986
+ swapMode: "ExactOut",
49987
+ slippageBps
49988
+ },
49989
+ transaction: {
49990
+ userPublicKey: new Uint8Array(32)
49991
+ // placeholder, not executing
49992
+ },
49993
+ update: {
49994
+ num_quotes: 1
49995
+ }
49996
+ });
49997
+ const reader = stream.getReader();
49998
+ const { value: swapQuotes, done } = await reader.read();
49999
+ reader.releaseLock();
50000
+ if (done || !swapQuotes) {
50001
+ throw new Error("Titan ExactOut estimate stream ended without data");
50002
+ }
50003
+ const quotes = swapQuotes;
50004
+ const bestRoute = selectBestRoute(quotes.quotes, "ExactOut");
50005
+ if (!bestRoute) {
50006
+ throw new Error(`No Titan ExactOut routes found for ${inputMint} -> ${outputMint}`);
50007
+ }
50008
+ const quoteResult = {
50009
+ ...buildSwapQuoteResult(bestRoute, "ExactOut"),
50010
+ provider: "TITAN" /* TITAN */
50011
+ };
50012
+ return {
50013
+ otherAmountThreshold: quoteResult.otherAmountThreshold,
50014
+ quoteResult
50015
+ };
50016
+ } finally {
50017
+ if (!client.closed) {
50018
+ await client.close();
50019
+ }
50020
+ }
50021
+ }
50022
+ async function getTitanExactOutViaHttpProxy(params) {
50023
+ const { inputMint, outputMint, amount, slippageBps, apiConfig } = params;
50024
+ const basePath = apiConfig?.basePath;
50025
+ if (!basePath) {
50026
+ throw new Error("Titan proxy URL is required (apiConfig.basePath)");
50027
+ }
50028
+ const response = await fetch(`${basePath}/exact-out-estimate`, {
50029
+ method: "POST",
50030
+ headers: {
50031
+ "Content-Type": "application/json",
50032
+ ...apiConfig?.headers ?? {}
50033
+ },
50034
+ body: JSON.stringify({
50035
+ inputMint,
50036
+ outputMint,
50037
+ amount,
50038
+ slippageBps
50039
+ })
50040
+ });
50041
+ if (!response.ok) {
50042
+ const errorData = await response.json().catch(() => ({}));
50043
+ throw new Error(
50044
+ `Titan proxy error (${response.status}): ${errorData.message ?? response.statusText}`
50045
+ );
50046
+ }
50047
+ const data = await response.json();
50048
+ const quoteResult = {
50049
+ inAmount: String(data.inAmount),
50050
+ outAmount: String(data.outAmount),
50051
+ otherAmountThreshold: data.otherAmountThreshold,
50052
+ slippageBps: data.slippageBps,
50053
+ provider: "TITAN" /* TITAN */
50054
+ };
50055
+ return {
50056
+ otherAmountThreshold: data.otherAmountThreshold,
50057
+ quoteResult
50058
+ };
50059
+ }
50060
+
50061
+ // src/services/account/utils/swap.utils.ts
50062
+ function getSwapProviderFn({
50063
+ attemptProvider,
50064
+ maxSwapTotalAccounts,
50065
+ inputMint,
50066
+ outputMint,
50067
+ amount,
50068
+ swapMode,
50069
+ authority,
50070
+ connection,
50071
+ destinationTokenAccount,
50072
+ swapOpts,
50073
+ sizeConstraint
50074
+ }) {
50075
+ switch (attemptProvider) {
50076
+ case "TITAN" /* TITAN */:
50077
+ return (apiConfig) => getTitanSwapIxsForFlashloan({
50078
+ quoteParams: {
50079
+ inputMint,
50080
+ outputMint,
50081
+ amount,
50082
+ swapMode,
50083
+ slippageBps: swapOpts.swapConfig?.slippageBps,
50084
+ platformFeeBps: swapOpts.swapConfig?.platformFeeBps,
50085
+ directRoutesOnly: swapOpts.swapConfig?.directRoutesOnly,
50086
+ sizeConstraint,
50087
+ maxSwapTotalAccounts
50088
+ },
50089
+ authority,
50090
+ connection,
50091
+ destinationTokenAccount,
50092
+ apiConfig
50093
+ });
50094
+ case "JUPITER" /* JUPITER */:
50095
+ return (apiConfig) => getJupiterSwapIxsForFlashloan({
50096
+ quoteParams: {
50097
+ inputMint,
50098
+ outputMint,
50099
+ amount,
50100
+ swapMode,
50101
+ dynamicSlippage: swapOpts.swapConfig ? swapOpts.swapConfig.slippageMode === "DYNAMIC" : true,
50102
+ slippageBps: swapOpts.swapConfig?.slippageBps,
50103
+ platformFeeBps: swapOpts.swapConfig?.platformFeeBps,
50104
+ onlyDirectRoutes: swapOpts.swapConfig?.directRoutesOnly ?? false
50105
+ },
50106
+ authority,
50107
+ connection,
50108
+ destinationTokenAccount,
50109
+ apiConfig,
50110
+ maxSwapAccounts: maxSwapTotalAccounts
50111
+ });
50112
+ default:
50113
+ return void 0;
50114
+ }
50115
+ }
50116
+ function getExactOutProviderFn({
50117
+ attemptProvider,
50118
+ inputMint,
50119
+ outputMint,
50120
+ amount,
50121
+ swapOpts,
50122
+ apiConfig
50123
+ }) {
50124
+ switch (attemptProvider) {
50125
+ case "TITAN" /* TITAN */:
50126
+ return () => getTitanExactOutEstimate({
50127
+ inputMint,
50128
+ outputMint,
50129
+ amount,
50130
+ slippageBps: swapOpts.swapConfig?.slippageBps,
50131
+ apiConfig
50132
+ });
50133
+ case "JUPITER" /* JUPITER */:
50134
+ return async () => {
50135
+ const configParams = toJupiterConfig(apiConfig);
50136
+ const jupiterApiClient = configParams?.basePath ? new api.SwapApi(new api.Configuration(configParams)) : api.createJupiterApiClient(configParams);
50137
+ const estimateQuote = await jupiterApiClient.quoteGet({
50138
+ inputMint,
50139
+ outputMint,
50140
+ amount,
50141
+ swapMode: "ExactOut",
50142
+ dynamicSlippage: swapOpts.swapConfig ? swapOpts.swapConfig.slippageMode === "DYNAMIC" : true,
50143
+ slippageBps: swapOpts.swapConfig?.slippageBps
50144
+ });
50145
+ const quoteResult = mapJupiterQuoteToSwapQuoteResult(estimateQuote);
50146
+ return { otherAmountThreshold: quoteResult.otherAmountThreshold, quoteResult };
50147
+ };
50148
+ default:
50149
+ return void 0;
50150
+ }
50151
+ }
50152
+ var getSwapIxsForFlashloan = async (params) => {
50153
+ const {
50154
+ inputMint,
50155
+ outputMint,
50156
+ amount,
50157
+ swapMode,
50158
+ authority,
50159
+ connection,
50160
+ destinationTokenAccount,
50161
+ swapOpts,
50162
+ sizeConstraint,
50163
+ maxSwapTotalAccounts
50164
+ } = params;
50165
+ if (swapOpts.swapIxs) {
50166
+ return {
50167
+ swapInstructions: swapOpts.swapIxs.instructions,
50168
+ setupInstructions: [],
50169
+ addressLookupTableAddresses: swapOpts.swapIxs.lookupTables,
50170
+ quoteResponse: {
50171
+ inAmount: String(amount),
50172
+ outAmount: "0",
50173
+ otherAmountThreshold: "0",
50174
+ slippageBps: 0
50175
+ }
50176
+ };
50177
+ }
50178
+ const provider = swapOpts.swapConfig?.provider ?? "JUPITER" /* JUPITER */;
50179
+ const attempts = [
50180
+ { provider, apiConfig: swapOpts.swapConfig?.apiConfig },
50181
+ ...swapOpts.swapConfig?.fallbackProviders ?? []
50182
+ ];
50183
+ let lastError;
50184
+ for (const { provider: attemptProvider, apiConfig } of attempts) {
50185
+ const fn = getSwapProviderFn({
50186
+ attemptProvider,
50187
+ maxSwapTotalAccounts: params.maxSwapTotalAccounts,
50188
+ inputMint,
50189
+ outputMint,
50190
+ amount,
50191
+ swapMode,
50192
+ authority,
50193
+ connection,
50194
+ destinationTokenAccount,
50195
+ swapOpts,
50196
+ sizeConstraint
50197
+ });
50198
+ if (!fn) continue;
50199
+ try {
50200
+ return await fn(apiConfig);
50201
+ } catch (err) {
50202
+ if (err instanceof TransactionBuildingError) throw err;
50203
+ lastError = err;
50204
+ console.warn(`[swap] ${attemptProvider} failed:`, err instanceof Error ? err.message : err);
50205
+ }
50206
+ }
50207
+ const firstProvider = attempts[0]?.provider ?? "Swap";
50208
+ throw TransactionBuildingError.swapQuoteFailed(
50209
+ firstProvider,
50210
+ inputMint,
50211
+ outputMint,
50212
+ lastError?.message ?? "No swap route available"
50213
+ );
50214
+ };
50215
+ var getExactOutEstimate = async (params) => {
50216
+ const { inputMint, outputMint, amount, swapOpts, connection } = params;
50217
+ const provider = swapOpts.swapConfig?.provider ?? "JUPITER" /* JUPITER */;
50218
+ const attempts = [
50219
+ { provider, apiConfig: swapOpts.swapConfig?.apiConfig },
50220
+ ...swapOpts.swapConfig?.fallbackProviders ?? []
50221
+ ];
50222
+ let lastError;
50223
+ for (const { provider: attemptProvider, apiConfig } of attempts) {
50224
+ const fn = getExactOutProviderFn({
50225
+ attemptProvider,
50226
+ inputMint,
50227
+ outputMint,
50228
+ amount,
50229
+ swapOpts,
50230
+ apiConfig
50231
+ });
50232
+ if (!fn) continue;
50233
+ try {
50234
+ return await fn(apiConfig);
50235
+ } catch (err) {
50236
+ if (err instanceof TransactionBuildingError) throw err;
50237
+ lastError = err;
50238
+ console.warn(
50239
+ `[exactout] ${attemptProvider} failed:`,
50240
+ err instanceof Error ? err.message : err
50241
+ );
50242
+ }
50243
+ }
50244
+ const firstProvider = attempts[0]?.provider ?? "Swap";
50245
+ throw TransactionBuildingError.swapQuoteFailed(
50246
+ firstProvider,
50247
+ inputMint,
50248
+ outputMint,
50249
+ lastError?.message ?? "No swap route available"
50250
+ );
50251
+ };
50252
+ function mapJupiterQuoteToSwapQuoteResult(quote) {
50253
+ return {
50254
+ inAmount: quote.inAmount,
50255
+ outAmount: quote.outAmount,
50256
+ otherAmountThreshold: quote.otherAmountThreshold,
50257
+ slippageBps: quote.slippageBps,
50258
+ platformFee: quote.platformFee ? {
50259
+ amount: quote.platformFee.amount ?? "0",
50260
+ feeBps: quote.platformFee.feeBps ?? 0
50261
+ } : void 0,
50262
+ priceImpactPct: quote.priceImpactPct,
50263
+ contextSlot: quote.contextSlot,
50264
+ timeTaken: quote.timeTaken,
50265
+ provider: "JUPITER" /* JUPITER */
50266
+ };
50267
+ }
50268
+
50269
+ // src/services/account/utils/jupiter.utils.ts
48888
50270
  var REFERRAL_PROGRAM_ID = new web3_js.PublicKey("REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3");
48889
50271
  var REFERRAL_ACCOUNT_PUBKEY = new web3_js.PublicKey("Mm7HcujSK2JzPW4eX7g4oqTXbWYDuFxapNMHXe8yp1B");
48890
50272
  var getFeeAccount = (mint) => {
48891
- const referralProgramPubkey = REFERRAL_PROGRAM_ID;
48892
- const referralAccountPubkey = REFERRAL_ACCOUNT_PUBKEY;
48893
50273
  const [feeAccount] = web3_js.PublicKey.findProgramAddressSync(
48894
- [Buffer.from("referral_ata"), referralAccountPubkey.toBuffer(), mint.toBuffer()],
48895
- referralProgramPubkey
50274
+ [Buffer.from("referral_ata"), REFERRAL_ACCOUNT_PUBKEY.toBuffer(), mint.toBuffer()],
50275
+ REFERRAL_PROGRAM_ID
48896
50276
  );
48897
50277
  return feeAccount.toBase58();
48898
50278
  };
50279
+ var checkFeeAccount = async (connection, mint) => {
50280
+ const feeAccount = getFeeAccount(mint);
50281
+ const hasFeeAccount = !!await connection.getAccountInfo(new web3_js.PublicKey(feeAccount));
50282
+ return { feeAccount, hasFeeAccount };
50283
+ };
48899
50284
  function deserializeJupiterInstruction(instruction) {
48900
50285
  return new web3_js.TransactionInstruction({
48901
50286
  programId: new web3_js.PublicKey(instruction.programId),
@@ -48907,17 +50292,26 @@ function deserializeJupiterInstruction(instruction) {
48907
50292
  data: Buffer.from(instruction.data, "base64")
48908
50293
  });
48909
50294
  }
50295
+ function toJupiterConfig(apiConfig) {
50296
+ if (!apiConfig) return void 0;
50297
+ return {
50298
+ basePath: apiConfig.basePath,
50299
+ apiKey: apiConfig.apiKey ? () => apiConfig.apiKey : void 0,
50300
+ headers: apiConfig.headers
50301
+ };
50302
+ }
48910
50303
  var getJupiterSwapIxsForFlashloan = async ({
48911
50304
  quoteParams,
48912
50305
  authority,
48913
50306
  connection,
48914
50307
  destinationTokenAccount,
48915
- configParams
50308
+ apiConfig,
50309
+ maxSwapAccounts
48916
50310
  }) => {
50311
+ const configParams = toJupiterConfig(apiConfig);
48917
50312
  const jupiterApiClient = configParams?.basePath ? new api.SwapApi(new api.Configuration(configParams)) : api.createJupiterApiClient(configParams);
48918
50313
  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));
50314
+ const { feeAccount, hasFeeAccount } = await checkFeeAccount(connection, new web3_js.PublicKey(feeMint));
48921
50315
  const project0JupiterLut = (await connection.getAddressLookupTable(ADDRESS_LOOKUP_TABLE_FOR_SWAP))?.value;
48922
50316
  let finalQuoteParams = quoteParams;
48923
50317
  if (!hasFeeAccount) {
@@ -48927,67 +50321,44 @@ var getJupiterSwapIxsForFlashloan = async ({
48927
50321
  platformFeeBps: void 0
48928
50322
  };
48929
50323
  }
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
- );
50324
+ const maxAccounts = maxSwapAccounts ?? 40;
50325
+ const swapQuote = await jupiterApiClient.quoteGet({
50326
+ ...finalQuoteParams,
50327
+ maxAccounts
50328
+ });
50329
+ const swapInstructionResponse = await jupiterApiClient.swapInstructionsPost({
50330
+ swapRequest: {
50331
+ quoteResponse: swapQuote,
50332
+ userPublicKey: authority.toBase58(),
50333
+ feeAccount: hasFeeAccount ? feeAccount : void 0,
50334
+ wrapAndUnwrapSol: false,
50335
+ destinationTokenAccount: destinationTokenAccount.toBase58()
50336
+ }
50337
+ });
50338
+ const lutAddresses = swapInstructionResponse.addressLookupTableAddresses;
48956
50339
  const lutAccountsRaw = await connection.getMultipleAccountsInfo(
48957
- lutAddresses.flat().map((address) => new web3_js.PublicKey(address))
50340
+ lutAddresses.map((address) => new web3_js.PublicKey(address))
48958
50341
  );
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
50342
+ const addressLookupTableAccounts = lutAccountsRaw.map((accountInfo, index) => {
50343
+ const addressLookupTableAddress = lutAddresses[index];
50344
+ if (!accountInfo || !addressLookupTableAddress) {
50345
+ return null;
50346
+ }
50347
+ return new web3_js.AddressLookupTableAccount({
50348
+ key: new web3_js.PublicKey(addressLookupTableAddress),
50349
+ state: web3_js.AddressLookupTableAccount.deserialize(accountInfo.data)
48988
50350
  });
48989
- }
48990
- return jupiterSwapIxs;
50351
+ }).filter((account) => account !== null).concat(project0JupiterLut ? [project0JupiterLut] : []);
50352
+ const instruction = deserializeJupiterInstruction(swapInstructionResponse.swapInstruction);
50353
+ const setupInstructions = swapInstructionResponse.setupInstructions.map(
50354
+ deserializeJupiterInstruction
50355
+ );
50356
+ return {
50357
+ swapInstructions: [instruction],
50358
+ setupInstructions,
50359
+ addressLookupTableAddresses: addressLookupTableAccounts,
50360
+ quoteResponse: mapJupiterQuoteToSwapQuoteResult(swapQuote)
50361
+ };
48991
50362
  };
48992
50363
 
48993
50364
  // src/services/account/utils/misc.utils.ts
@@ -50208,7 +51579,7 @@ function computeMaxLeverage(depositBank, borrowBank, opts) {
50208
51579
  ltv
50209
51580
  };
50210
51581
  }
50211
- function computeLoopingParams(principal, targetLeverage, depositBank, borrowBank, depositOracleInfo, borrowOracleInfo, opts) {
51582
+ function computeLoopingParams(principal, targetLeverage, depositBank, borrowBank, depositPriceUsd, borrowPriceUsd, opts) {
50212
51583
  const initialCollateral = toBigNumber(principal);
50213
51584
  const { maxLeverage } = computeMaxLeverage(depositBank, borrowBank, opts);
50214
51585
  let clampedLeverage = targetLeverage;
@@ -50223,7 +51594,7 @@ function computeLoopingParams(principal, targetLeverage, depositBank, borrowBank
50223
51594
  }
50224
51595
  const totalDepositAmount = initialCollateral.times(new BigNumber3__default.default(clampedLeverage));
50225
51596
  const additionalDepositAmount = totalDepositAmount.minus(initialCollateral);
50226
- const totalBorrowAmount = additionalDepositAmount.times(depositOracleInfo.priceWeighted.lowestPrice).div(borrowOracleInfo.priceWeighted.highestPrice);
51597
+ const totalBorrowAmount = additionalDepositAmount.times(new BigNumber3__default.default(depositPriceUsd)).div(new BigNumber3__default.default(borrowPriceUsd));
50227
51598
  return {
50228
51599
  totalBorrowAmount: totalBorrowAmount.decimalPlaces(
50229
51600
  borrowBank.mintDecimals,
@@ -52690,7 +54061,7 @@ var MarginfiAccount = class _MarginfiAccount {
52690
54061
  * @param params.oraclePrices - Map of current oracle prices
52691
54062
  * @param params.depositOpts - Deposit configuration (bank, amount, mode)
52692
54063
  * @param params.borrowOpts - Borrow configuration (bank, amount)
52693
- * @param params.swapOpts - Jupiter swap configuration (slippage, fees)
54064
+ * @param params.swapOpts - Swap configuration (venue, slippage, fees)
52694
54065
  * @param params.addressLookupTableAccounts - Address lookup tables
52695
54066
  * @param params.overrideInferAccounts - Optional account overrides
52696
54067
  * @param params.additionalIxs - Additional instructions to include
@@ -52728,7 +54099,7 @@ var MarginfiAccount = class _MarginfiAccount {
52728
54099
  * @param params.oraclePrices - Map of current oracle prices
52729
54100
  * @param params.withdrawOpts - Withdraw configuration (bank, amount)
52730
54101
  * @param params.repayOpts - Repay configuration (bank, optional amount)
52731
- * @param params.swapOpts - Jupiter swap configuration
54102
+ * @param params.swapOpts - Swap configuration (venue, slippage, fees)
52732
54103
  * @param params.addressLookupTableAccounts - Address lookup tables
52733
54104
  * @param params.overrideInferAccounts - Optional account overrides
52734
54105
  * @param params.additionalIxs - Additional instructions to include
@@ -52768,7 +54139,7 @@ var MarginfiAccount = class _MarginfiAccount {
52768
54139
  * @param params.oraclePrices - Map of current oracle prices
52769
54140
  * @param params.withdrawOpts - Withdraw configuration (bank, amount, tokenProgram)
52770
54141
  * @param params.depositOpts - Deposit configuration (bank, tokenProgram)
52771
- * @param params.swapOpts - Jupiter swap configuration
54142
+ * @param params.swapOpts - Swap configuration (venue, slippage, fees)
52772
54143
  * @param params.addressLookupTableAccounts - Address lookup tables
52773
54144
  * @param params.overrideInferAccounts - Optional account overrides
52774
54145
  * @param params.additionalIxs - Additional instructions to include
@@ -52808,7 +54179,7 @@ var MarginfiAccount = class _MarginfiAccount {
52808
54179
  * @param params.oraclePrices - Map of current oracle prices
52809
54180
  * @param params.repayOpts - Repay configuration (bank, amount, tokenProgram)
52810
54181
  * @param params.borrowOpts - Borrow configuration (bank, tokenProgram)
52811
- * @param params.swapOpts - Jupiter swap configuration
54182
+ * @param params.swapOpts - Swap configuration (venue, slippage, fees)
52812
54183
  * @param params.addressLookupTableAccounts - Address lookup tables
52813
54184
  * @param params.overrideInferAccounts - Optional account overrides
52814
54185
  * @param params.additionalIxs - Additional instructions to include
@@ -54156,6 +55527,7 @@ exports.MARGINFI_PROGRAM = MARGINFI_PROGRAM;
54156
55527
  exports.MARGINFI_PROGRAM_STAGING = MARGINFI_PROGRAM_STAGING;
54157
55528
  exports.MARGINFI_PROGRAM_STAGING_ALT = MARGINFI_PROGRAM_STAGING_ALT;
54158
55529
  exports.MARGINFI_SPONSORED_SHARD_ID = MARGINFI_SPONSORED_SHARD_ID;
55530
+ exports.MAX_ACCOUNT_LOCKS = MAX_ACCOUNT_LOCKS;
54159
55531
  exports.MAX_CONFIDENCE_INTERVAL_RATIO = MAX_CONFIDENCE_INTERVAL_RATIO;
54160
55532
  exports.MAX_TX_SIZE = MAX_TX_SIZE;
54161
55533
  exports.MAX_U64 = MAX_U64;
@@ -54191,6 +55563,7 @@ exports.SYSTEM_PROGRAM_ID = SYSTEM_PROGRAM_ID;
54191
55563
  exports.SYSVAR_CLOCK_ID = SYSVAR_CLOCK_ID;
54192
55564
  exports.SYSVAR_RENT_ID = SYSVAR_RENT_ID;
54193
55565
  exports.SYSVAR_STAKE_HISTORY_ID = SYSVAR_STAKE_HISTORY_ID;
55566
+ exports.SwapProvider = SwapProvider;
54194
55567
  exports.TRANSFER_ACCOUNT_AUTHORITY_FLAG = TRANSFER_ACCOUNT_AUTHORITY_FLAG;
54195
55568
  exports.TransactionArenaKeyMap = TransactionArenaKeyMap;
54196
55569
  exports.TransactionBuildingError = TransactionBuildingError;
@@ -54224,6 +55597,7 @@ exports.checkMultipleOraclesCrankability = checkMultipleOraclesCrankability;
54224
55597
  exports.chunkedGetRawMultipleAccountInfoOrdered = chunkedGetRawMultipleAccountInfoOrdered;
54225
55598
  exports.chunkedGetRawMultipleAccountInfoOrderedWithNulls = chunkedGetRawMultipleAccountInfoOrderedWithNulls;
54226
55599
  exports.chunkedGetRawMultipleAccountInfos = chunkedGetRawMultipleAccountInfos;
55600
+ exports.compileFlashloanPrecheck = compileFlashloanPrecheck;
54227
55601
  exports.composeRemainingAccounts = composeRemainingAccounts;
54228
55602
  exports.computeAccountValue = computeAccountValue;
54229
55603
  exports.computeActiveEmodePairs = computeActiveEmodePairs;
@@ -54234,6 +55608,8 @@ exports.computeBaseInterestRate = computeBaseInterestRate;
54234
55608
  exports.computeClaimedEmissions = computeClaimedEmissions;
54235
55609
  exports.computeClosePositionTokenAmount = computeClosePositionTokenAmount;
54236
55610
  exports.computeEmodeImpacts = computeEmodeImpacts;
55611
+ exports.computeFlashLoanNonSwapBudget = computeFlashLoanNonSwapBudget;
55612
+ exports.computeFlashloanSwapConstraints = computeFlashloanSwapConstraints;
54237
55613
  exports.computeFreeCollateralFromBalances = computeFreeCollateralFromBalances;
54238
55614
  exports.computeFreeCollateralFromCache = computeFreeCollateralFromCache;
54239
55615
  exports.computeHealthAccountMetas = computeHealthAccountMetas;
@@ -54262,6 +55638,7 @@ exports.computeTotalOutstandingEmissions = computeTotalOutstandingEmissions;
54262
55638
  exports.computeTvl = computeTvl;
54263
55639
  exports.computeUsdValue = computeUsdValue;
54264
55640
  exports.computeUtilizationRate = computeUtilizationRate;
55641
+ exports.computeV0TxSize = computeV0TxSize;
54265
55642
  exports.convertVoteAccCoeffsToBankCoeffs = convertVoteAccCoeffsToBankCoeffs;
54266
55643
  exports.createActiveEmodePairFromPairs = createActiveEmodePairFromPairs;
54267
55644
  exports.createEmptyBalance = createEmptyBalance;
@@ -54337,6 +55714,7 @@ exports.getDriftCTokenMultiplier = getDriftCTokenMultiplier;
54337
55714
  exports.getDriftMetadata = getDriftMetadata;
54338
55715
  exports.getDriftStatesDto = getDriftStatesDto;
54339
55716
  exports.getEmodePairs = getEmodePairs;
55717
+ exports.getExactOutEstimate = getExactOutEstimate;
54340
55718
  exports.getHealthCacheStatusDescription = getHealthCacheStatusDescription;
54341
55719
  exports.getHealthSimulationTransactions = getHealthSimulationTransactions;
54342
55720
  exports.getJupLendFTokenMultiplier = getJupLendFTokenMultiplier;
@@ -54355,10 +55733,15 @@ exports.getOracleSourceNameFromKey = getOracleSourceNameFromKey;
54355
55733
  exports.getPrice = getPrice;
54356
55734
  exports.getPriceWithConfidence = getPriceWithConfidence;
54357
55735
  exports.getStakedBankMetadataMap = getStakedBankMetadataMap;
55736
+ exports.getSwapIxsForFlashloan = getSwapIxsForFlashloan;
55737
+ exports.getTitanExactOutEstimate = getTitanExactOutEstimate;
55738
+ exports.getTitanSwapIxsForFlashloan = getTitanSwapIxsForFlashloan;
55739
+ exports.getTotalAccountKeys = getTotalAccountKeys;
54358
55740
  exports.getTotalAssetQuantity = getTotalAssetQuantity;
54359
55741
  exports.getTotalLiabilityQuantity = getTotalLiabilityQuantity;
54360
55742
  exports.getTxSize = getTxSize;
54361
55743
  exports.getValidatorVoteAccountByBank = getValidatorVoteAccountByBank;
55744
+ exports.getWritableAccountKeys = getWritableAccountKeys;
54362
55745
  exports.groupToDto = groupToDto;
54363
55746
  exports.hasAccountFlag = hasAccountFlag;
54364
55747
  exports.hasEmodeEntryFlag = hasEmodeEntryFlag;
@@ -54427,6 +55810,7 @@ exports.makeWithdrawIx = makeWithdrawIx3;
54427
55810
  exports.makeWithdrawTx = makeWithdrawTx;
54428
55811
  exports.makeWrapSolIxs = makeWrapSolIxs;
54429
55812
  exports.mapBrokenFeedsToOraclePrices = mapBrokenFeedsToOraclePrices;
55813
+ exports.mapJupiterQuoteToSwapQuoteResult = mapJupiterQuoteToSwapQuoteResult;
54430
55814
  exports.mapPythBanksToOraclePrices = mapPythBanksToOraclePrices;
54431
55815
  exports.mapSwbBanksToOraclePrices = mapSwbBanksToOraclePrices;
54432
55816
  exports.marginfiAccountToDto = marginfiAccountToDto;
@@ -54463,6 +55847,7 @@ exports.toBankDto = toBankDto;
54463
55847
  exports.toBigNumber = toBigNumber;
54464
55848
  exports.toEmodeSettingsDto = toEmodeSettingsDto;
54465
55849
  exports.toInterestRateConfigDto = toInterestRateConfigDto;
55850
+ exports.toJupiterConfig = toJupiterConfig;
54466
55851
  exports.toNumber = toNumber;
54467
55852
  exports.uiToNative = uiToNative;
54468
55853
  exports.uiToNativeBigNumber = uiToNativeBigNumber;