@0dotxyz/p0-ts-sdk 2.5.5-alpha.4 → 2.5.5-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2648 -2129
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +285 -32
- package/dist/index.d.ts +285 -32
- package/dist/index.js +2637 -2128
- package/dist/index.js.map +1 -1
- package/dist/vendor.cjs +1 -0
- package/dist/vendor.cjs.map +1 -1
- package/dist/vendor.js +1 -0
- package/dist/vendor.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -131,6 +131,7 @@ var TransactionBuildingErrorCode = /* @__PURE__ */ ((TransactionBuildingErrorCod
|
|
|
131
131
|
TransactionBuildingErrorCode2["TRANSFER_POSITIONS_INVALID_SELECTION"] = "TRANSFER_POSITIONS_INVALID_SELECTION";
|
|
132
132
|
TransactionBuildingErrorCode2["TRANSFER_POSITIONS_UNSUPPORTED_BANK"] = "TRANSFER_POSITIONS_UNSUPPORTED_BANK";
|
|
133
133
|
TransactionBuildingErrorCode2["TRANSFER_POSITIONS_UNSPLITTABLE"] = "TRANSFER_POSITIONS_UNSPLITTABLE";
|
|
134
|
+
TransactionBuildingErrorCode2["BRIDGE_CONFLICT"] = "BRIDGE_CONFLICT";
|
|
134
135
|
return TransactionBuildingErrorCode2;
|
|
135
136
|
})(TransactionBuildingErrorCode || {});
|
|
136
137
|
var TransactionBuildingError = class _TransactionBuildingError extends Error {
|
|
@@ -261,6 +262,19 @@ var TransactionBuildingError = class _TransactionBuildingError extends Error {
|
|
|
261
262
|
{ reason, sizeBytes, accountCount }
|
|
262
263
|
);
|
|
263
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* A bridged (double-hop) swap could not route because every bridge-token candidate bank
|
|
267
|
+
* conflicts with an existing opposite-side position on the account (marginfi forbids holding an
|
|
268
|
+
* asset and a liability on the same bank).
|
|
269
|
+
*/
|
|
270
|
+
static bridgeConflict(conflictingBanks, bridgeTokenSide) {
|
|
271
|
+
const banksList = conflictingBanks.map((c) => c.symbol ?? c.mint).join(", ");
|
|
272
|
+
return new _TransactionBuildingError(
|
|
273
|
+
"BRIDGE_CONFLICT" /* BRIDGE_CONFLICT */,
|
|
274
|
+
`Every bridge-token candidate conflicts with an existing opposite-side position: ${banksList}`,
|
|
275
|
+
{ conflictingBanks, bridgeTokenSide }
|
|
276
|
+
);
|
|
277
|
+
}
|
|
264
278
|
/**
|
|
265
279
|
* Generic escape hatch for custom errors
|
|
266
280
|
*/
|
|
@@ -277,6 +291,9 @@ var DECOMPOSABLE_SWAP_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
277
291
|
function isDecomposableSwapError(e) {
|
|
278
292
|
return e instanceof TransactionBuildingError && DECOMPOSABLE_SWAP_ERROR_CODES.has(e.code);
|
|
279
293
|
}
|
|
294
|
+
function isBridgeConflictError(e) {
|
|
295
|
+
return e instanceof TransactionBuildingError && e.code === "BRIDGE_CONFLICT" /* BRIDGE_CONFLICT */;
|
|
296
|
+
}
|
|
280
297
|
var PDA_BANK_LIQUIDITY_VAULT_AUTH_SEED = Buffer.from("liquidity_vault_auth");
|
|
281
298
|
var PDA_BANK_INSURANCE_VAULT_AUTH_SEED = Buffer.from("insurance_vault_auth");
|
|
282
299
|
var PDA_BANK_FEE_VAULT_AUTH_SEED = Buffer.from("fee_vault_auth");
|
|
@@ -436,6 +453,7 @@ var PRIORITY_TX_SIZE = 44;
|
|
|
436
453
|
var WSOL_MINT = new web3_js.PublicKey("So11111111111111111111111111111111111111112");
|
|
437
454
|
var LST_MINT = new web3_js.PublicKey("LSTxxxnJzKDFSLr4dUkPcmCf5VyryEqzPLz5j4bpxFp");
|
|
438
455
|
var USDC_MINT = new web3_js.PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
|
|
456
|
+
var USDT_MINT = new web3_js.PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB");
|
|
439
457
|
var USDC_DECIMALS = 6;
|
|
440
458
|
|
|
441
459
|
// src/utils/accounting.utils.ts
|
|
@@ -67237,1775 +67255,298 @@ function annotateFit(route, req) {
|
|
|
67237
67255
|
};
|
|
67238
67256
|
}
|
|
67239
67257
|
|
|
67240
|
-
// src/
|
|
67241
|
-
|
|
67242
|
-
|
|
67243
|
-
|
|
67244
|
-
|
|
67245
|
-
|
|
67246
|
-
|
|
67247
|
-
|
|
67248
|
-
|
|
67249
|
-
addressLookupTableAccounts,
|
|
67250
|
-
connection,
|
|
67251
|
-
oraclePrices,
|
|
67252
|
-
crossbarUrl,
|
|
67253
|
-
additionalIxs = []
|
|
67254
|
-
} = params;
|
|
67255
|
-
const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
|
|
67256
|
-
const setupIxs = await makeSetupIx({
|
|
67257
|
-
connection,
|
|
67258
|
-
authority: marginfiAccount.authority,
|
|
67259
|
-
tokens: [
|
|
67260
|
-
{
|
|
67261
|
-
mint: borrowOpts.borrowBank.mint,
|
|
67262
|
-
tokenProgram: borrowOpts.tokenProgram
|
|
67263
|
-
},
|
|
67264
|
-
{
|
|
67265
|
-
mint: depositOpts.depositBank.mint,
|
|
67266
|
-
tokenProgram: depositOpts.tokenProgram
|
|
67267
|
-
}
|
|
67268
|
-
]
|
|
67269
|
-
});
|
|
67270
|
-
const refreshIntegrationIxs = makeRefreshIntegrationBanksIxs(
|
|
67271
|
-
params.marginfiAccount,
|
|
67272
|
-
params.bankMap,
|
|
67273
|
-
[depositOpts.depositBank.address],
|
|
67274
|
-
params.bankMetadataMap,
|
|
67275
|
-
[borrowOpts.borrowBank.address, depositOpts.depositBank.address]
|
|
67276
|
-
);
|
|
67277
|
-
const { flashloanTx, setupInstructions, swapQuote, depositIxs, borrowIxs } = await buildLoopFlashloanTx({
|
|
67278
|
-
...params,
|
|
67279
|
-
blockhash
|
|
67280
|
-
});
|
|
67281
|
-
const jupiterSetupInstructions = setupInstructions.filter((ix) => {
|
|
67282
|
-
if (ix.programId.equals(web3_js.ComputeBudgetProgram.programId)) {
|
|
67283
|
-
return false;
|
|
67284
|
-
}
|
|
67285
|
-
if (ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
|
|
67286
|
-
const mintKey = ix.keys[3]?.pubkey;
|
|
67287
|
-
if (mintKey?.equals(depositOpts.depositBank.mint) || mintKey?.equals(borrowOpts.borrowBank.mint)) {
|
|
67288
|
-
return false;
|
|
67289
|
-
}
|
|
67290
|
-
}
|
|
67291
|
-
return true;
|
|
67292
|
-
});
|
|
67293
|
-
setupIxs.push(...jupiterSetupInstructions);
|
|
67294
|
-
const { instructions: updateFeedIxs, luts: feedLuts } = await makeSmartCrankSwbFeedIx({
|
|
67295
|
-
marginfiAccount,
|
|
67296
|
-
bankMap,
|
|
67297
|
-
oraclePrices,
|
|
67298
|
-
assetShareValueMultiplierByBank: params.assetShareValueMultiplierByBank,
|
|
67299
|
-
instructions: [...borrowIxs.instructions, ...depositIxs.instructions],
|
|
67300
|
-
program,
|
|
67301
|
-
connection,
|
|
67302
|
-
crossbarUrl
|
|
67303
|
-
});
|
|
67304
|
-
let additionalTxs = [];
|
|
67305
|
-
if (depositOpts.depositBank.mint.equals(NATIVE_MINT) && depositOpts.inputDepositAmount) {
|
|
67306
|
-
setupIxs.push(
|
|
67307
|
-
...makeWrapSolIxs(marginfiAccount.authority, new BigNumber3.BigNumber(depositOpts.inputDepositAmount))
|
|
67308
|
-
);
|
|
67258
|
+
// src/models/balance.ts
|
|
67259
|
+
var Balance = class _Balance {
|
|
67260
|
+
constructor(active, bankPk, assetShares, liabilityShares, emissionsOutstanding, lastUpdate) {
|
|
67261
|
+
this.active = active;
|
|
67262
|
+
this.bankPk = bankPk;
|
|
67263
|
+
this.assetShares = assetShares;
|
|
67264
|
+
this.liabilityShares = liabilityShares;
|
|
67265
|
+
this.emissionsOutstanding = emissionsOutstanding;
|
|
67266
|
+
this.lastUpdate = lastUpdate;
|
|
67309
67267
|
}
|
|
67310
|
-
|
|
67311
|
-
const
|
|
67312
|
-
|
|
67313
|
-
|
|
67314
|
-
|
|
67315
|
-
|
|
67316
|
-
|
|
67317
|
-
|
|
67318
|
-
|
|
67319
|
-
(tx) => addTransactionMetadata(tx, {
|
|
67320
|
-
type: "CREATE_ATA" /* CREATE_ATA */,
|
|
67321
|
-
addressLookupTables: addressLookupTableAccounts
|
|
67322
|
-
})
|
|
67323
|
-
)
|
|
67268
|
+
static from(balanceRaw) {
|
|
67269
|
+
const props = parseBalanceRaw(balanceRaw);
|
|
67270
|
+
return new _Balance(
|
|
67271
|
+
props.active,
|
|
67272
|
+
props.bankPk,
|
|
67273
|
+
props.assetShares,
|
|
67274
|
+
props.liabilityShares,
|
|
67275
|
+
props.emissionsOutstanding,
|
|
67276
|
+
props.lastUpdate
|
|
67324
67277
|
);
|
|
67325
67278
|
}
|
|
67326
|
-
|
|
67327
|
-
|
|
67328
|
-
|
|
67329
|
-
|
|
67330
|
-
|
|
67331
|
-
|
|
67332
|
-
|
|
67333
|
-
|
|
67334
|
-
addressLookupTables: feedLuts,
|
|
67335
|
-
type: "CRANK" /* CRANK */
|
|
67336
|
-
})
|
|
67279
|
+
static fromBalanceType(balance) {
|
|
67280
|
+
return new _Balance(
|
|
67281
|
+
balance.active,
|
|
67282
|
+
balance.bankPk,
|
|
67283
|
+
balance.assetShares,
|
|
67284
|
+
balance.liabilityShares,
|
|
67285
|
+
balance.emissionsOutstanding,
|
|
67286
|
+
balance.lastUpdate
|
|
67337
67287
|
);
|
|
67338
67288
|
}
|
|
67339
|
-
|
|
67340
|
-
|
|
67341
|
-
|
|
67342
|
-
|
|
67343
|
-
|
|
67344
|
-
|
|
67345
|
-
|
|
67346
|
-
|
|
67347
|
-
|
|
67348
|
-
|
|
67349
|
-
|
|
67350
|
-
|
|
67351
|
-
params,
|
|
67352
|
-
innerIxs: descriptor.innerIxs,
|
|
67353
|
-
luts: descriptor.luts,
|
|
67354
|
-
swapIxCount: 0,
|
|
67355
|
-
swapLutCount: 0,
|
|
67356
|
-
sizeConstraint: descriptor.sizeConstraint
|
|
67289
|
+
static createEmpty(bankPk) {
|
|
67290
|
+
const balance = createEmptyBalance(bankPk);
|
|
67291
|
+
return this.fromBalanceType(balance);
|
|
67292
|
+
}
|
|
67293
|
+
computeUsdValue(bank, oraclePrice, marginRequirement = 2 /* Equity */, assetShareValueMultiplier, activeEmodeWeights) {
|
|
67294
|
+
return computeBalanceUsdValue({
|
|
67295
|
+
balance: this,
|
|
67296
|
+
bank,
|
|
67297
|
+
oraclePrice,
|
|
67298
|
+
marginRequirement,
|
|
67299
|
+
assetShareValueMultiplier,
|
|
67300
|
+
activeEmodeWeights
|
|
67357
67301
|
});
|
|
67358
|
-
return {
|
|
67359
|
-
flashloanTx: flashloanTx2,
|
|
67360
|
-
setupInstructions: [],
|
|
67361
|
-
swapQuote: void 0,
|
|
67362
|
-
borrowIxs,
|
|
67363
|
-
depositIxs
|
|
67364
|
-
};
|
|
67365
67302
|
}
|
|
67366
|
-
|
|
67367
|
-
|
|
67368
|
-
|
|
67369
|
-
|
|
67370
|
-
|
|
67371
|
-
|
|
67372
|
-
|
|
67373
|
-
|
|
67374
|
-
const flashloanTx = await finalizeLoopFlashloanTx({
|
|
67375
|
-
params,
|
|
67376
|
-
innerIxs: finalIxs,
|
|
67377
|
-
luts,
|
|
67378
|
-
swapIxCount: engineResult.swapInstructions.length,
|
|
67379
|
-
swapLutCount: engineResult.swapLuts.length,
|
|
67380
|
-
sizeConstraint: descriptor.sizeConstraint
|
|
67381
|
-
});
|
|
67382
|
-
return {
|
|
67383
|
-
flashloanTx,
|
|
67384
|
-
setupInstructions: engineResult.setupInstructions,
|
|
67385
|
-
swapQuote: engineResult.quoteResponse,
|
|
67386
|
-
borrowIxs,
|
|
67387
|
-
depositIxs
|
|
67388
|
-
};
|
|
67389
|
-
}
|
|
67390
|
-
async function buildLoopNonSwapIxs(params) {
|
|
67391
|
-
const {
|
|
67392
|
-
program,
|
|
67393
|
-
marginfiAccount,
|
|
67394
|
-
bankMap,
|
|
67395
|
-
borrowOpts,
|
|
67396
|
-
depositOpts,
|
|
67397
|
-
bankMetadataMap,
|
|
67398
|
-
addressLookupTableAccounts,
|
|
67399
|
-
overrideInferAccounts
|
|
67400
|
-
} = params;
|
|
67401
|
-
const cuRequestIxs = [
|
|
67402
|
-
web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
|
|
67403
|
-
web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
|
|
67404
|
-
];
|
|
67405
|
-
const swapNeeded = !depositOpts.depositBank.mint.equals(borrowOpts.borrowBank.mint);
|
|
67406
|
-
const destinationTokenAccount = getAssociatedTokenAddressSync(
|
|
67407
|
-
new web3_js.PublicKey(depositOpts.depositBank.mint),
|
|
67408
|
-
marginfiAccount.authority,
|
|
67409
|
-
true,
|
|
67410
|
-
depositOpts.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
|
|
67411
|
-
);
|
|
67412
|
-
const principalUi = depositOpts.loopMode === "DEPOSIT" ? depositOpts.inputDepositAmount : 0;
|
|
67413
|
-
let depositAmountUi;
|
|
67414
|
-
let sizeConstraint = 0;
|
|
67415
|
-
let maxSwapTotalAccounts = 0;
|
|
67416
|
-
if (!swapNeeded) {
|
|
67417
|
-
depositAmountUi = borrowOpts.borrowAmount + principalUi;
|
|
67418
|
-
} else {
|
|
67419
|
-
const swapConstraints = await computeFlashloanSwapConstraints({
|
|
67420
|
-
program,
|
|
67421
|
-
marginfiAccount,
|
|
67422
|
-
bankMap,
|
|
67423
|
-
bankMetadataMap,
|
|
67424
|
-
addressLookupTableAccounts: addressLookupTableAccounts ?? [],
|
|
67425
|
-
primaryIx: {
|
|
67426
|
-
type: "borrow",
|
|
67427
|
-
bank: borrowOpts.borrowBank,
|
|
67428
|
-
tokenProgram: borrowOpts.tokenProgram
|
|
67429
|
-
},
|
|
67430
|
-
secondaryIx: {
|
|
67431
|
-
type: "deposit",
|
|
67432
|
-
bank: depositOpts.depositBank,
|
|
67433
|
-
tokenProgram: depositOpts.tokenProgram
|
|
67434
|
-
},
|
|
67435
|
-
overrideInferAccounts
|
|
67303
|
+
getUsdValueWithPriceBias(bank, oraclePrice, marginRequirement = 2 /* Equity */, assetShareValueMultiplier, activeEmodeWeights) {
|
|
67304
|
+
return getBalanceUsdValueWithPriceBias({
|
|
67305
|
+
balance: this,
|
|
67306
|
+
bank,
|
|
67307
|
+
oraclePrice,
|
|
67308
|
+
marginRequirement,
|
|
67309
|
+
assetShareValueMultiplier,
|
|
67310
|
+
activeEmodeWeights
|
|
67436
67311
|
});
|
|
67437
|
-
sizeConstraint = swapConstraints.sizeConstraint;
|
|
67438
|
-
maxSwapTotalAccounts = swapConstraints.maxSwapTotalAccounts;
|
|
67439
|
-
const estimateUi = borrowOpts.borrowAmount * borrowOpts.marketPrice / depositOpts.marketPrice;
|
|
67440
|
-
depositAmountUi = estimateUi + principalUi;
|
|
67441
67312
|
}
|
|
67442
|
-
|
|
67443
|
-
|
|
67444
|
-
bank: borrowOpts.borrowBank,
|
|
67445
|
-
bankMap,
|
|
67446
|
-
tokenProgram: borrowOpts.tokenProgram,
|
|
67447
|
-
amount: borrowOpts.borrowAmount,
|
|
67448
|
-
marginfiAccount,
|
|
67449
|
-
authority: marginfiAccount.authority,
|
|
67450
|
-
isSync: false,
|
|
67451
|
-
opts: {
|
|
67452
|
-
createAtas: false,
|
|
67453
|
-
wrapAndUnwrapSol: false,
|
|
67454
|
-
overrideInferAccounts
|
|
67455
|
-
}
|
|
67456
|
-
});
|
|
67457
|
-
const depositIxs = await buildDepositIxs(params, depositAmountUi);
|
|
67458
|
-
const innerIxsBeforeSwap = [...cuRequestIxs, ...borrowIxs.instructions];
|
|
67459
|
-
const swapSlotIndex = innerIxsBeforeSwap.length;
|
|
67460
|
-
const innerIxs = [...innerIxsBeforeSwap, ...depositIxs.instructions];
|
|
67461
|
-
const depositIxIndex = innerIxs.findIndex(isDepositIx);
|
|
67462
|
-
if (depositIxIndex < 0) {
|
|
67463
|
-
throw new Error(
|
|
67464
|
-
"buildLoopNonSwapIxs: could not locate deposit instruction for amount patching"
|
|
67465
|
-
);
|
|
67313
|
+
computeQuantity(bank) {
|
|
67314
|
+
return computeQuantity(this, bank);
|
|
67466
67315
|
}
|
|
67467
|
-
|
|
67468
|
-
|
|
67469
|
-
swapSlotIndex,
|
|
67470
|
-
depositIxIndex,
|
|
67471
|
-
inputMint: borrowOpts.borrowBank.mint.toBase58(),
|
|
67472
|
-
outputMint: depositOpts.depositBank.mint.toBase58(),
|
|
67473
|
-
inputDecimals: borrowOpts.borrowBank.mintDecimals,
|
|
67474
|
-
outputDecimals: depositOpts.depositBank.mintDecimals,
|
|
67475
|
-
inAmountNative: uiToNative(
|
|
67476
|
-
borrowOpts.borrowAmount,
|
|
67477
|
-
borrowOpts.borrowBank.mintDecimals
|
|
67478
|
-
).toNumber(),
|
|
67479
|
-
destinationTokenAccount,
|
|
67480
|
-
sizeConstraint,
|
|
67481
|
-
maxSwapTotalAccounts,
|
|
67482
|
-
luts: addressLookupTableAccounts ?? []
|
|
67483
|
-
};
|
|
67484
|
-
return { descriptor, swapNeeded, borrowIxs, depositIxs };
|
|
67485
|
-
}
|
|
67486
|
-
async function buildDepositIxs(params, amountUi) {
|
|
67487
|
-
const { program, marginfiAccount, depositOpts, bankMetadataMap, overrideInferAccounts } = params;
|
|
67488
|
-
switch (depositOpts.depositBank.config.assetTag) {
|
|
67489
|
-
case 3 /* KAMINO */: {
|
|
67490
|
-
const reserve = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.kaminoStates?.reserveState;
|
|
67491
|
-
if (!reserve) {
|
|
67492
|
-
throw TransactionBuildingError.kaminoReserveNotFound(
|
|
67493
|
-
depositOpts.depositBank.address.toBase58(),
|
|
67494
|
-
depositOpts.depositBank.mint.toBase58(),
|
|
67495
|
-
depositOpts.depositBank.tokenSymbol
|
|
67496
|
-
);
|
|
67497
|
-
}
|
|
67498
|
-
return makeKaminoDepositIx3({
|
|
67499
|
-
program,
|
|
67500
|
-
bank: depositOpts.depositBank,
|
|
67501
|
-
tokenProgram: depositOpts.tokenProgram,
|
|
67502
|
-
amount: amountUi,
|
|
67503
|
-
accountAddress: marginfiAccount.address,
|
|
67504
|
-
authority: marginfiAccount.authority,
|
|
67505
|
-
group: marginfiAccount.group,
|
|
67506
|
-
reserve,
|
|
67507
|
-
opts: {
|
|
67508
|
-
wrapAndUnwrapSol: false,
|
|
67509
|
-
overrideInferAccounts
|
|
67510
|
-
}
|
|
67511
|
-
});
|
|
67512
|
-
}
|
|
67513
|
-
case 4 /* DRIFT */: {
|
|
67514
|
-
const driftState = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.driftStates;
|
|
67515
|
-
if (!driftState) {
|
|
67516
|
-
throw TransactionBuildingError.driftStateNotFound(
|
|
67517
|
-
depositOpts.depositBank.address.toBase58(),
|
|
67518
|
-
depositOpts.depositBank.mint.toBase58(),
|
|
67519
|
-
depositOpts.depositBank.tokenSymbol
|
|
67520
|
-
);
|
|
67521
|
-
}
|
|
67522
|
-
return makeDriftDepositIx3({
|
|
67523
|
-
program,
|
|
67524
|
-
bank: depositOpts.depositBank,
|
|
67525
|
-
tokenProgram: depositOpts.tokenProgram,
|
|
67526
|
-
amount: amountUi,
|
|
67527
|
-
accountAddress: marginfiAccount.address,
|
|
67528
|
-
authority: marginfiAccount.authority,
|
|
67529
|
-
group: marginfiAccount.group,
|
|
67530
|
-
driftMarketIndex: driftState.spotMarketState.marketIndex,
|
|
67531
|
-
driftOracle: driftState.spotMarketState.oracle,
|
|
67532
|
-
opts: {
|
|
67533
|
-
wrapAndUnwrapSol: false,
|
|
67534
|
-
overrideInferAccounts
|
|
67535
|
-
}
|
|
67536
|
-
});
|
|
67537
|
-
}
|
|
67538
|
-
case 6 /* JUPLEND */: {
|
|
67539
|
-
return makeJuplendDepositIx2({
|
|
67540
|
-
program,
|
|
67541
|
-
bank: depositOpts.depositBank,
|
|
67542
|
-
tokenProgram: depositOpts.tokenProgram,
|
|
67543
|
-
amount: amountUi,
|
|
67544
|
-
accountAddress: marginfiAccount.address,
|
|
67545
|
-
authority: marginfiAccount.authority,
|
|
67546
|
-
group: marginfiAccount.group,
|
|
67547
|
-
opts: {
|
|
67548
|
-
wrapAndUnwrapSol: false,
|
|
67549
|
-
overrideInferAccounts
|
|
67550
|
-
}
|
|
67551
|
-
});
|
|
67552
|
-
}
|
|
67553
|
-
default: {
|
|
67554
|
-
return makeDepositIx3({
|
|
67555
|
-
program,
|
|
67556
|
-
bank: depositOpts.depositBank,
|
|
67557
|
-
tokenProgram: depositOpts.tokenProgram,
|
|
67558
|
-
amount: amountUi,
|
|
67559
|
-
accountAddress: marginfiAccount.address,
|
|
67560
|
-
authority: marginfiAccount.authority,
|
|
67561
|
-
group: marginfiAccount.group,
|
|
67562
|
-
opts: {
|
|
67563
|
-
wrapAndUnwrapSol: false,
|
|
67564
|
-
overrideInferAccounts
|
|
67565
|
-
}
|
|
67566
|
-
});
|
|
67567
|
-
}
|
|
67316
|
+
computeQuantityUi(bank, assetShareValueMultiplier) {
|
|
67317
|
+
return computeQuantityUi(this, bank, assetShareValueMultiplier);
|
|
67568
67318
|
}
|
|
67569
|
-
|
|
67570
|
-
|
|
67571
|
-
const { connection, swapOpts, marginfiAccount, swapEngineRunner } = params;
|
|
67572
|
-
if (swapOpts.swapIxs) {
|
|
67573
|
-
return {
|
|
67574
|
-
swapInstructions: swapOpts.swapIxs.instructions,
|
|
67575
|
-
setupInstructions: [],
|
|
67576
|
-
swapLuts: swapOpts.swapIxs.lookupTables,
|
|
67577
|
-
quoteResponse: {
|
|
67578
|
-
inAmount: String(descriptor.inAmountNative),
|
|
67579
|
-
outAmount: "0",
|
|
67580
|
-
otherAmountThreshold: "0",
|
|
67581
|
-
slippageBps: 0
|
|
67582
|
-
},
|
|
67583
|
-
outputAmountNative: new BN9__default.default(0)
|
|
67584
|
-
};
|
|
67319
|
+
computeTotalOutstandingEmissions(bank) {
|
|
67320
|
+
return computeTotalOutstandingEmissions(this, bank);
|
|
67585
67321
|
}
|
|
67586
|
-
|
|
67587
|
-
|
|
67588
|
-
inputMint: descriptor.inputMint,
|
|
67589
|
-
outputMint: descriptor.outputMint,
|
|
67590
|
-
amountNative: descriptor.inAmountNative,
|
|
67591
|
-
inputDecimals: descriptor.inputDecimals,
|
|
67592
|
-
outputDecimals: descriptor.outputDecimals,
|
|
67593
|
-
...swapEngineQuoteFieldsFromOpts(swapOpts),
|
|
67594
|
-
taker: marginfiAccount.authority,
|
|
67595
|
-
destinationTokenAccount: descriptor.destinationTokenAccount,
|
|
67596
|
-
connection,
|
|
67597
|
-
footprint: {
|
|
67598
|
-
instructions: descriptor.innerIxs,
|
|
67599
|
-
luts: descriptor.luts,
|
|
67600
|
-
payer: marginfiAccount.authority,
|
|
67601
|
-
sizeConstraint: descriptor.sizeConstraint,
|
|
67602
|
-
maxSwapTotalAccounts: descriptor.maxSwapTotalAccounts
|
|
67603
|
-
},
|
|
67604
|
-
providers: swapEngineProvidersFromOpts(swapOpts)
|
|
67605
|
-
});
|
|
67606
|
-
return {
|
|
67607
|
-
swapInstructions: engineResult.swapInstructions,
|
|
67608
|
-
setupInstructions: engineResult.setupInstructions,
|
|
67609
|
-
swapLuts: engineResult.swapLuts,
|
|
67610
|
-
quoteResponse: engineResult.quoteResponse,
|
|
67611
|
-
outputAmountNative: engineResult.outputAmountNative
|
|
67612
|
-
};
|
|
67613
|
-
}
|
|
67614
|
-
async function finalizeLoopFlashloanTx({
|
|
67615
|
-
params,
|
|
67616
|
-
innerIxs,
|
|
67617
|
-
luts,
|
|
67618
|
-
swapIxCount,
|
|
67619
|
-
swapLutCount,
|
|
67620
|
-
sizeConstraint
|
|
67621
|
-
}) {
|
|
67622
|
-
const { program, marginfiAccount, bankMap, swapOpts, blockhash } = params;
|
|
67623
|
-
if (swapIxCount > 0) {
|
|
67624
|
-
compileFlashloanPrecheck({
|
|
67625
|
-
allIxs: innerIxs,
|
|
67626
|
-
payer: marginfiAccount.authority,
|
|
67627
|
-
luts,
|
|
67628
|
-
sizeConstraint,
|
|
67629
|
-
swapIxCount,
|
|
67630
|
-
swapLutCount
|
|
67631
|
-
});
|
|
67322
|
+
computeClaimedEmissions(bank, currentTimestamp) {
|
|
67323
|
+
return computeClaimedEmissions(this, bank, currentTimestamp);
|
|
67632
67324
|
}
|
|
67633
|
-
|
|
67634
|
-
|
|
67635
|
-
|
|
67636
|
-
|
|
67637
|
-
|
|
67638
|
-
|
|
67639
|
-
|
|
67640
|
-
|
|
67641
|
-
|
|
67642
|
-
|
|
67643
|
-
|
|
67644
|
-
|
|
67645
|
-
|
|
67646
|
-
|
|
67647
|
-
|
|
67325
|
+
};
|
|
67326
|
+
|
|
67327
|
+
// src/models/health-cache.ts
|
|
67328
|
+
var HealthCache = class _HealthCache {
|
|
67329
|
+
constructor(assetValue, liabilityValue, assetValueMaint, liabilityValueMaint, assetValueEquity, liabilityValueEquity, timestamp, flags, prices, simulationStatus) {
|
|
67330
|
+
this.assetValue = assetValue;
|
|
67331
|
+
this.liabilityValue = liabilityValue;
|
|
67332
|
+
this.assetValueMaint = assetValueMaint;
|
|
67333
|
+
this.liabilityValueMaint = liabilityValueMaint;
|
|
67334
|
+
this.assetValueEquity = assetValueEquity;
|
|
67335
|
+
this.liabilityValueEquity = liabilityValueEquity;
|
|
67336
|
+
this.timestamp = timestamp;
|
|
67337
|
+
this.flags = flags;
|
|
67338
|
+
this.prices = prices;
|
|
67339
|
+
this.simulationStatus = simulationStatus;
|
|
67340
|
+
}
|
|
67341
|
+
static fromHealthCacheType(healthCacheType) {
|
|
67342
|
+
return new _HealthCache(
|
|
67343
|
+
healthCacheType.assetValue,
|
|
67344
|
+
healthCacheType.liabilityValue,
|
|
67345
|
+
healthCacheType.assetValueMaint,
|
|
67346
|
+
healthCacheType.liabilityValueMaint,
|
|
67347
|
+
healthCacheType.assetValueEquity,
|
|
67348
|
+
healthCacheType.liabilityValueEquity,
|
|
67349
|
+
healthCacheType.timestamp,
|
|
67350
|
+
healthCacheType.flags,
|
|
67351
|
+
healthCacheType.prices,
|
|
67352
|
+
healthCacheType.simulationStatus
|
|
67648
67353
|
);
|
|
67649
67354
|
}
|
|
67650
|
-
|
|
67651
|
-
|
|
67652
|
-
async function makeSwapCollateralTx(params) {
|
|
67653
|
-
const {
|
|
67654
|
-
program,
|
|
67655
|
-
marginfiAccount,
|
|
67656
|
-
connection,
|
|
67657
|
-
bankMap,
|
|
67658
|
-
oraclePrices,
|
|
67659
|
-
withdrawOpts,
|
|
67660
|
-
depositOpts,
|
|
67661
|
-
bankMetadataMap,
|
|
67662
|
-
assetShareValueMultiplierByBank,
|
|
67663
|
-
addressLookupTableAccounts,
|
|
67664
|
-
crossbarUrl,
|
|
67665
|
-
additionalIxs = []
|
|
67666
|
-
} = params;
|
|
67667
|
-
const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
|
|
67668
|
-
const setupIxs = await makeSetupIx({
|
|
67669
|
-
connection,
|
|
67670
|
-
authority: marginfiAccount.authority,
|
|
67671
|
-
tokens: [
|
|
67672
|
-
{ mint: withdrawOpts.withdrawBank.mint, tokenProgram: withdrawOpts.tokenProgram },
|
|
67673
|
-
{ mint: depositOpts.depositBank.mint, tokenProgram: depositOpts.tokenProgram }
|
|
67674
|
-
]
|
|
67675
|
-
});
|
|
67676
|
-
const refreshIntegrationIxs = makeRefreshIntegrationBanksIxs(
|
|
67677
|
-
marginfiAccount,
|
|
67678
|
-
bankMap,
|
|
67679
|
-
[withdrawOpts.withdrawBank.address, depositOpts.depositBank.address],
|
|
67680
|
-
bankMetadataMap
|
|
67681
|
-
);
|
|
67682
|
-
const { flashloanTx, setupInstructions, swapQuote, withdrawIxs, depositIxs } = await buildSwapCollateralFlashloanTx({
|
|
67683
|
-
...params,
|
|
67684
|
-
blockhash
|
|
67685
|
-
});
|
|
67686
|
-
const jupiterSetupInstructions = setupInstructions.filter((ix) => {
|
|
67687
|
-
if (ix.programId.equals(web3_js.ComputeBudgetProgram.programId)) {
|
|
67688
|
-
return false;
|
|
67689
|
-
}
|
|
67690
|
-
if (ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
|
|
67691
|
-
const mintKey = ix.keys[3]?.pubkey;
|
|
67692
|
-
if (mintKey?.equals(withdrawOpts.withdrawBank.mint) || mintKey?.equals(depositOpts.depositBank.mint)) {
|
|
67693
|
-
return false;
|
|
67694
|
-
}
|
|
67695
|
-
}
|
|
67696
|
-
return true;
|
|
67697
|
-
});
|
|
67698
|
-
setupIxs.push(...jupiterSetupInstructions);
|
|
67699
|
-
const { instructions: updateFeedIxs, luts: feedLuts } = await makeSmartCrankSwbFeedIx({
|
|
67700
|
-
marginfiAccount,
|
|
67701
|
-
bankMap,
|
|
67702
|
-
oraclePrices,
|
|
67703
|
-
assetShareValueMultiplierByBank,
|
|
67704
|
-
instructions: [...withdrawIxs.instructions, ...depositIxs.instructions],
|
|
67705
|
-
program,
|
|
67706
|
-
connection,
|
|
67707
|
-
crossbarUrl
|
|
67708
|
-
});
|
|
67709
|
-
let additionalTxs = [];
|
|
67710
|
-
if (setupIxs.length > 0 || refreshIntegrationIxs.instructions.length > 0) {
|
|
67711
|
-
const ixs = [...setupIxs, ...refreshIntegrationIxs.instructions];
|
|
67712
|
-
const txs = splitInstructionsToFitTransactions([], ixs, {
|
|
67713
|
-
blockhash,
|
|
67714
|
-
payerKey: marginfiAccount.authority,
|
|
67715
|
-
luts: addressLookupTableAccounts ?? []
|
|
67716
|
-
});
|
|
67717
|
-
additionalTxs.push(
|
|
67718
|
-
...txs.map(
|
|
67719
|
-
(tx) => addTransactionMetadata(tx, {
|
|
67720
|
-
type: "CREATE_ATA" /* CREATE_ATA */,
|
|
67721
|
-
addressLookupTables: addressLookupTableAccounts
|
|
67722
|
-
})
|
|
67723
|
-
)
|
|
67724
|
-
);
|
|
67355
|
+
static from(healthCacheRaw) {
|
|
67356
|
+
return this.fromHealthCacheType(parseHealthCacheRaw(healthCacheRaw));
|
|
67725
67357
|
}
|
|
67726
|
-
|
|
67727
|
-
|
|
67728
|
-
|
|
67729
|
-
|
|
67730
|
-
|
|
67731
|
-
|
|
67732
|
-
|
|
67733
|
-
|
|
67734
|
-
|
|
67735
|
-
|
|
67736
|
-
|
|
67737
|
-
|
|
67358
|
+
};
|
|
67359
|
+
|
|
67360
|
+
// src/models/account.ts
|
|
67361
|
+
var MarginfiAccount = class _MarginfiAccount {
|
|
67362
|
+
constructor(address, group, authority, balances, accountFlags, emissionsDestinationAccount, healthCache) {
|
|
67363
|
+
this.address = address;
|
|
67364
|
+
this.group = group;
|
|
67365
|
+
this.authority = authority;
|
|
67366
|
+
this.balances = balances;
|
|
67367
|
+
this.accountFlags = accountFlags;
|
|
67368
|
+
this.emissionsDestinationAccount = emissionsDestinationAccount;
|
|
67369
|
+
this.healthCache = healthCache;
|
|
67738
67370
|
}
|
|
67739
|
-
|
|
67740
|
-
|
|
67741
|
-
|
|
67742
|
-
|
|
67743
|
-
|
|
67744
|
-
|
|
67745
|
-
|
|
67746
|
-
|
|
67747
|
-
program
|
|
67748
|
-
|
|
67749
|
-
|
|
67750
|
-
withdrawOpts,
|
|
67751
|
-
depositOpts,
|
|
67752
|
-
swapOpts,
|
|
67753
|
-
bankMetadataMap,
|
|
67754
|
-
assetShareValueMultiplierByBank,
|
|
67755
|
-
addressLookupTableAccounts,
|
|
67756
|
-
connection,
|
|
67757
|
-
overrideInferAccounts,
|
|
67758
|
-
blockhash,
|
|
67759
|
-
swapEngineRunner
|
|
67760
|
-
}) {
|
|
67761
|
-
const {
|
|
67762
|
-
withdrawBank,
|
|
67763
|
-
tokenProgram: withdrawTokenProgram,
|
|
67764
|
-
totalPositionAmount,
|
|
67765
|
-
withdrawAmount
|
|
67766
|
-
} = withdrawOpts;
|
|
67767
|
-
const { depositBank, tokenProgram: depositTokenProgram } = depositOpts;
|
|
67768
|
-
if (withdrawAmount !== void 0 && withdrawAmount <= 0) {
|
|
67769
|
-
throw new Error("withdrawAmount must be greater than 0");
|
|
67371
|
+
/**
|
|
67372
|
+
* Fetches a marginfi account from on-chain data.
|
|
67373
|
+
*
|
|
67374
|
+
* @param address - The public key of the marginfi account
|
|
67375
|
+
* @param program - The Marginfi program instance
|
|
67376
|
+
*
|
|
67377
|
+
* @returns Promise resolving to a MarginfiAccount instance
|
|
67378
|
+
*/
|
|
67379
|
+
static async fetch(address, program) {
|
|
67380
|
+
const data = await program.account.marginfiAccount.fetch(address);
|
|
67381
|
+
return _MarginfiAccount.fromAccountParsed(address, data);
|
|
67770
67382
|
}
|
|
67771
|
-
|
|
67772
|
-
|
|
67773
|
-
{ amount: totalPositionAmount, isLending: true },
|
|
67774
|
-
actualWithdrawAmount,
|
|
67775
|
-
withdrawBank.mintDecimals
|
|
67776
|
-
);
|
|
67777
|
-
const cuRequestIxs = [
|
|
67778
|
-
web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
|
|
67779
|
-
web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
|
|
67780
|
-
];
|
|
67781
|
-
let amountToDeposit;
|
|
67782
|
-
let swapInstructions = [];
|
|
67783
|
-
let setupInstructions = [];
|
|
67784
|
-
let swapLookupTables = [];
|
|
67785
|
-
let swapQuote;
|
|
67786
|
-
let sizeConstraintUsed = 0;
|
|
67787
|
-
let withdrawIxs;
|
|
67788
|
-
switch (withdrawOpts.withdrawBank.config.assetTag) {
|
|
67789
|
-
case 3 /* KAMINO */: {
|
|
67790
|
-
const reserve = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.kaminoStates?.reserveState;
|
|
67791
|
-
if (!reserve) {
|
|
67792
|
-
throw TransactionBuildingError.kaminoReserveNotFound(
|
|
67793
|
-
withdrawOpts.withdrawBank.address.toBase58(),
|
|
67794
|
-
withdrawOpts.withdrawBank.mint.toBase58(),
|
|
67795
|
-
withdrawOpts.withdrawBank.tokenSymbol
|
|
67796
|
-
);
|
|
67797
|
-
}
|
|
67798
|
-
const multiplier = assetShareValueMultiplierByBank.get(withdrawOpts.withdrawBank.address.toBase58()) ?? new BigNumber3.BigNumber(1);
|
|
67799
|
-
const adjustedAmount = new BigNumber3.BigNumber(actualWithdrawAmount).div(multiplier).times(1.0001).toNumber();
|
|
67800
|
-
withdrawIxs = await makeKaminoWithdrawIx3({
|
|
67801
|
-
program,
|
|
67802
|
-
bank: withdrawBank,
|
|
67803
|
-
bankMap,
|
|
67804
|
-
tokenProgram: withdrawTokenProgram,
|
|
67805
|
-
cTokenAmount: adjustedAmount,
|
|
67806
|
-
marginfiAccount,
|
|
67807
|
-
authority: marginfiAccount.authority,
|
|
67808
|
-
reserve,
|
|
67809
|
-
withdrawAll: isFullWithdraw,
|
|
67810
|
-
isSync: false,
|
|
67811
|
-
opts: {
|
|
67812
|
-
createAtas: false,
|
|
67813
|
-
wrapAndUnwrapSol: false,
|
|
67814
|
-
overrideInferAccounts
|
|
67815
|
-
}
|
|
67816
|
-
});
|
|
67817
|
-
break;
|
|
67818
|
-
}
|
|
67819
|
-
case 4 /* DRIFT */: {
|
|
67820
|
-
const driftState = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.driftStates;
|
|
67821
|
-
if (!driftState) {
|
|
67822
|
-
throw TransactionBuildingError.driftStateNotFound(
|
|
67823
|
-
withdrawOpts.withdrawBank.address.toBase58(),
|
|
67824
|
-
withdrawOpts.withdrawBank.mint.toBase58(),
|
|
67825
|
-
withdrawOpts.withdrawBank.tokenSymbol
|
|
67826
|
-
);
|
|
67827
|
-
}
|
|
67828
|
-
withdrawIxs = await makeDriftWithdrawIx3({
|
|
67829
|
-
program,
|
|
67830
|
-
bank: withdrawOpts.withdrawBank,
|
|
67831
|
-
bankMap,
|
|
67832
|
-
tokenProgram: withdrawOpts.tokenProgram,
|
|
67833
|
-
amount: actualWithdrawAmount,
|
|
67834
|
-
marginfiAccount,
|
|
67835
|
-
authority: marginfiAccount.authority,
|
|
67836
|
-
driftSpotMarket: driftState.spotMarketState,
|
|
67837
|
-
userRewards: driftState.userRewards,
|
|
67838
|
-
withdrawAll: isFullWithdraw,
|
|
67839
|
-
isSync: false,
|
|
67840
|
-
opts: {
|
|
67841
|
-
createAtas: false,
|
|
67842
|
-
wrapAndUnwrapSol: false,
|
|
67843
|
-
overrideInferAccounts
|
|
67844
|
-
}
|
|
67845
|
-
});
|
|
67846
|
-
break;
|
|
67847
|
-
}
|
|
67848
|
-
case 6 /* JUPLEND */: {
|
|
67849
|
-
const jupLendState = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.jupLendStates;
|
|
67850
|
-
if (!jupLendState) {
|
|
67851
|
-
throw TransactionBuildingError.jupLendStateNotFound(
|
|
67852
|
-
withdrawOpts.withdrawBank.address.toBase58(),
|
|
67853
|
-
withdrawOpts.withdrawBank.mint.toBase58(),
|
|
67854
|
-
withdrawOpts.withdrawBank.tokenSymbol
|
|
67855
|
-
);
|
|
67856
|
-
}
|
|
67857
|
-
withdrawIxs = await makeJuplendWithdrawIx2({
|
|
67858
|
-
program,
|
|
67859
|
-
bank: withdrawBank,
|
|
67860
|
-
bankMap,
|
|
67861
|
-
tokenProgram: withdrawTokenProgram,
|
|
67862
|
-
amount: actualWithdrawAmount,
|
|
67863
|
-
marginfiAccount,
|
|
67864
|
-
authority: marginfiAccount.authority,
|
|
67865
|
-
jupLendingState: jupLendState.jupLendingState,
|
|
67866
|
-
withdrawAll: isFullWithdraw,
|
|
67867
|
-
opts: {
|
|
67868
|
-
createAtas: false,
|
|
67869
|
-
wrapAndUnwrapSol: false,
|
|
67870
|
-
overrideInferAccounts
|
|
67871
|
-
}
|
|
67872
|
-
});
|
|
67873
|
-
break;
|
|
67874
|
-
}
|
|
67875
|
-
default: {
|
|
67876
|
-
withdrawIxs = await makeWithdrawIx3({
|
|
67877
|
-
program,
|
|
67878
|
-
bank: withdrawBank,
|
|
67879
|
-
bankMap,
|
|
67880
|
-
tokenProgram: withdrawTokenProgram,
|
|
67881
|
-
amount: actualWithdrawAmount,
|
|
67882
|
-
marginfiAccount,
|
|
67883
|
-
authority: marginfiAccount.authority,
|
|
67884
|
-
withdrawAll: isFullWithdraw,
|
|
67885
|
-
isSync: false,
|
|
67886
|
-
opts: {
|
|
67887
|
-
createAtas: false,
|
|
67888
|
-
wrapAndUnwrapSol: false,
|
|
67889
|
-
overrideInferAccounts
|
|
67890
|
-
}
|
|
67891
|
-
});
|
|
67892
|
-
break;
|
|
67893
|
-
}
|
|
67383
|
+
static decodeAccountRaw(encoded, idl) {
|
|
67384
|
+
return decodeAccountRaw(encoded, idl);
|
|
67894
67385
|
}
|
|
67895
|
-
|
|
67896
|
-
|
|
67897
|
-
|
|
67898
|
-
|
|
67899
|
-
|
|
67900
|
-
|
|
67901
|
-
|
|
67902
|
-
|
|
67903
|
-
|
|
67904
|
-
|
|
67905
|
-
depositBank.tokenSymbol
|
|
67906
|
-
);
|
|
67907
|
-
}
|
|
67908
|
-
depositIxs = await makeKaminoDepositIx3({
|
|
67909
|
-
program,
|
|
67910
|
-
bank: depositBank,
|
|
67911
|
-
tokenProgram: depositTokenProgram,
|
|
67912
|
-
amount: amountToDeposit,
|
|
67913
|
-
accountAddress: marginfiAccount.address,
|
|
67914
|
-
authority: marginfiAccount.authority,
|
|
67915
|
-
group: marginfiAccount.group,
|
|
67916
|
-
reserve,
|
|
67917
|
-
opts: {
|
|
67918
|
-
wrapAndUnwrapSol: false,
|
|
67919
|
-
overrideInferAccounts
|
|
67920
|
-
}
|
|
67921
|
-
});
|
|
67922
|
-
break;
|
|
67923
|
-
}
|
|
67924
|
-
case 4 /* DRIFT */: {
|
|
67925
|
-
const driftState = bankMetadataMap[depositBank.address.toBase58()]?.driftStates;
|
|
67926
|
-
if (!driftState) {
|
|
67927
|
-
throw TransactionBuildingError.driftStateNotFound(
|
|
67928
|
-
depositBank.address.toBase58(),
|
|
67929
|
-
depositBank.mint.toBase58(),
|
|
67930
|
-
depositBank.tokenSymbol
|
|
67931
|
-
);
|
|
67932
|
-
}
|
|
67933
|
-
const driftMarketIndex = driftState.spotMarketState.marketIndex;
|
|
67934
|
-
const driftOracle = driftState.spotMarketState.oracle;
|
|
67935
|
-
depositIxs = await makeDriftDepositIx3({
|
|
67936
|
-
program,
|
|
67937
|
-
bank: depositBank,
|
|
67938
|
-
tokenProgram: depositTokenProgram,
|
|
67939
|
-
amount: amountToDeposit,
|
|
67940
|
-
accountAddress: marginfiAccount.address,
|
|
67941
|
-
authority: marginfiAccount.authority,
|
|
67942
|
-
group: marginfiAccount.group,
|
|
67943
|
-
driftMarketIndex,
|
|
67944
|
-
driftOracle,
|
|
67945
|
-
opts: {
|
|
67946
|
-
wrapAndUnwrapSol: false,
|
|
67947
|
-
overrideInferAccounts
|
|
67948
|
-
}
|
|
67949
|
-
});
|
|
67950
|
-
break;
|
|
67951
|
-
}
|
|
67952
|
-
case 6 /* JUPLEND */: {
|
|
67953
|
-
depositIxs = await makeJuplendDepositIx2({
|
|
67954
|
-
program,
|
|
67955
|
-
bank: depositBank,
|
|
67956
|
-
tokenProgram: depositTokenProgram,
|
|
67957
|
-
amount: amountToDeposit,
|
|
67958
|
-
accountAddress: marginfiAccount.address,
|
|
67959
|
-
authority: marginfiAccount.authority,
|
|
67960
|
-
group: marginfiAccount.group,
|
|
67961
|
-
opts: {
|
|
67962
|
-
wrapAndUnwrapSol: false,
|
|
67963
|
-
overrideInferAccounts
|
|
67964
|
-
}
|
|
67965
|
-
});
|
|
67966
|
-
break;
|
|
67967
|
-
}
|
|
67968
|
-
default: {
|
|
67969
|
-
depositIxs = await makeDepositIx3({
|
|
67970
|
-
program,
|
|
67971
|
-
bank: depositBank,
|
|
67972
|
-
tokenProgram: depositTokenProgram,
|
|
67973
|
-
amount: amountToDeposit,
|
|
67974
|
-
accountAddress: marginfiAccount.address,
|
|
67975
|
-
authority: marginfiAccount.authority,
|
|
67976
|
-
group: marginfiAccount.group,
|
|
67977
|
-
opts: {
|
|
67978
|
-
wrapAndUnwrapSol: false,
|
|
67979
|
-
overrideInferAccounts
|
|
67980
|
-
}
|
|
67981
|
-
});
|
|
67982
|
-
break;
|
|
67983
|
-
}
|
|
67386
|
+
static fromAccountType(account) {
|
|
67387
|
+
return new _MarginfiAccount(
|
|
67388
|
+
account.address,
|
|
67389
|
+
account.group,
|
|
67390
|
+
account.authority,
|
|
67391
|
+
account.balances.map((b) => Balance.fromBalanceType(b)),
|
|
67392
|
+
account.accountFlags,
|
|
67393
|
+
account.emissionsDestinationAccount,
|
|
67394
|
+
account.healthCache
|
|
67395
|
+
);
|
|
67984
67396
|
}
|
|
67985
|
-
|
|
67986
|
-
|
|
67987
|
-
|
|
67988
|
-
|
|
67989
|
-
|
|
67990
|
-
|
|
67397
|
+
/**
|
|
67398
|
+
* Creates a MarginfiAccount instance from parsed account data.
|
|
67399
|
+
*
|
|
67400
|
+
* @param marginfiAccountPk - The public key of the marginfi account
|
|
67401
|
+
* @param accountData - The raw account data from the program
|
|
67402
|
+
*
|
|
67403
|
+
* @returns A new MarginfiAccount instance
|
|
67404
|
+
*/
|
|
67405
|
+
static fromAccountParsed(marginfiAccountPk, accountData) {
|
|
67406
|
+
const props = parseMarginfiAccountRaw(marginfiAccountPk, accountData);
|
|
67407
|
+
return new _MarginfiAccount(
|
|
67408
|
+
props.address,
|
|
67409
|
+
props.group,
|
|
67410
|
+
props.authority,
|
|
67411
|
+
props.balances.map((b) => Balance.fromBalanceType(b)),
|
|
67412
|
+
props.accountFlags,
|
|
67413
|
+
props.emissionsDestinationAccount,
|
|
67414
|
+
HealthCache.fromHealthCacheType(props.healthCache)
|
|
67991
67415
|
);
|
|
67992
|
-
const swapConstraints = await computeFlashloanSwapConstraints({
|
|
67993
|
-
program,
|
|
67994
|
-
marginfiAccount,
|
|
67995
|
-
bankMap,
|
|
67996
|
-
bankMetadataMap,
|
|
67997
|
-
addressLookupTableAccounts: addressLookupTableAccounts ?? [],
|
|
67998
|
-
primaryIx: { type: "withdraw", bank: withdrawBank, tokenProgram: withdrawTokenProgram },
|
|
67999
|
-
secondaryIx: { type: "deposit", bank: depositBank, tokenProgram: depositTokenProgram },
|
|
68000
|
-
overrideInferAccounts
|
|
68001
|
-
});
|
|
68002
|
-
sizeConstraintUsed = swapConstraints.sizeConstraint;
|
|
68003
|
-
const runEngine = swapEngineRunner ?? runSwapEngine;
|
|
68004
|
-
const engineResult = await runEngine({
|
|
68005
|
-
inputMint: withdrawBank.mint.toBase58(),
|
|
68006
|
-
outputMint: depositBank.mint.toBase58(),
|
|
68007
|
-
amountNative: uiToNative(actualWithdrawAmount, withdrawBank.mintDecimals).toNumber(),
|
|
68008
|
-
inputDecimals: withdrawBank.mintDecimals,
|
|
68009
|
-
outputDecimals: depositBank.mintDecimals,
|
|
68010
|
-
...swapEngineQuoteFieldsFromOpts(swapOpts),
|
|
68011
|
-
taker: marginfiAccount.authority,
|
|
68012
|
-
destinationTokenAccount,
|
|
68013
|
-
connection,
|
|
68014
|
-
footprint: {
|
|
68015
|
-
instructions: [...cuRequestIxs, ...withdrawIxs.instructions, ...depositIxs.instructions],
|
|
68016
|
-
luts: addressLookupTableAccounts ?? [],
|
|
68017
|
-
payer: marginfiAccount.authority,
|
|
68018
|
-
sizeConstraint: swapConstraints.sizeConstraint,
|
|
68019
|
-
maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
|
|
68020
|
-
},
|
|
68021
|
-
providers: swapEngineProvidersFromOpts(swapOpts)
|
|
68022
|
-
});
|
|
68023
|
-
const depositIxToPatch = depositIxs.instructions.find(isDepositIx);
|
|
68024
|
-
if (!depositIxToPatch) {
|
|
68025
|
-
throw new Error("swap-collateral: could not locate deposit instruction for amount patching");
|
|
68026
|
-
}
|
|
68027
|
-
patchDepositAmount(depositIxToPatch, engineResult.outputAmountNative);
|
|
68028
|
-
swapInstructions = engineResult.swapInstructions;
|
|
68029
|
-
setupInstructions = engineResult.setupInstructions;
|
|
68030
|
-
swapLookupTables = engineResult.swapLuts;
|
|
68031
|
-
swapQuote = engineResult.quoteResponse;
|
|
68032
67416
|
}
|
|
68033
|
-
|
|
68034
|
-
|
|
68035
|
-
|
|
68036
|
-
|
|
68037
|
-
|
|
68038
|
-
|
|
68039
|
-
|
|
68040
|
-
|
|
68041
|
-
|
|
68042
|
-
|
|
68043
|
-
|
|
68044
|
-
|
|
68045
|
-
|
|
68046
|
-
swapIxCount: swapInstructions.length,
|
|
68047
|
-
swapLutCount: swapLookupTables.length
|
|
67417
|
+
/**
|
|
67418
|
+
* Simulates and updates the health cache for this account.
|
|
67419
|
+
*
|
|
67420
|
+
* Fetches current oracle prices and computes fresh health values. Returns a new
|
|
67421
|
+
* account instance with updated health cache (does not mutate this instance).
|
|
67422
|
+
*
|
|
67423
|
+
* @param params - Configuration for health cache simulation (excluding marginfiAccount)
|
|
67424
|
+
* @returns Object containing the updated account and any errors
|
|
67425
|
+
*/
|
|
67426
|
+
async simulateHealthCache(params) {
|
|
67427
|
+
const accountWithHealthCache = await simulateAccountHealthCacheWithFallback({
|
|
67428
|
+
marginfiAccount: this,
|
|
67429
|
+
...params
|
|
68048
67430
|
});
|
|
67431
|
+
return {
|
|
67432
|
+
account: _MarginfiAccount.fromAccountType(accountWithHealthCache.marginfiAccount),
|
|
67433
|
+
error: accountWithHealthCache.error
|
|
67434
|
+
};
|
|
68049
67435
|
}
|
|
68050
|
-
|
|
68051
|
-
|
|
68052
|
-
|
|
68053
|
-
bankMap,
|
|
68054
|
-
addressLookupTableAccounts: luts,
|
|
68055
|
-
blockhash,
|
|
68056
|
-
ixs: allNonFlIxs,
|
|
68057
|
-
isSync: false
|
|
68058
|
-
});
|
|
68059
|
-
const txSize = getTxSize(flashloanTx);
|
|
68060
|
-
const totalKeys = getTotalAccountKeys(flashloanTx);
|
|
68061
|
-
if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
|
|
68062
|
-
throw TransactionBuildingError.swapSizeExceededPositionSwap(
|
|
68063
|
-
txSize,
|
|
68064
|
-
totalKeys,
|
|
68065
|
-
swapOpts.swapConfig?.provider
|
|
68066
|
-
);
|
|
67436
|
+
static fromAccountDataRaw(marginfiAccountPk, rawData, idl) {
|
|
67437
|
+
const marginfiAccountData = _MarginfiAccount.decodeAccountRaw(rawData, idl);
|
|
67438
|
+
return _MarginfiAccount.fromAccountParsed(marginfiAccountPk, marginfiAccountData);
|
|
68067
67439
|
}
|
|
68068
|
-
|
|
68069
|
-
|
|
68070
|
-
|
|
68071
|
-
|
|
68072
|
-
|
|
68073
|
-
|
|
68074
|
-
|
|
68075
|
-
|
|
68076
|
-
|
|
68077
|
-
|
|
68078
|
-
program,
|
|
68079
|
-
marginfiAccount,
|
|
68080
|
-
connection,
|
|
68081
|
-
bankMap,
|
|
68082
|
-
oraclePrices,
|
|
68083
|
-
repayOpts,
|
|
68084
|
-
borrowOpts,
|
|
68085
|
-
bankMetadataMap,
|
|
68086
|
-
assetShareValueMultiplierByBank,
|
|
68087
|
-
addressLookupTableAccounts,
|
|
68088
|
-
crossbarUrl,
|
|
68089
|
-
additionalIxs = []
|
|
68090
|
-
} = params;
|
|
68091
|
-
const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
|
|
68092
|
-
const setupIxs = await makeSetupIx({
|
|
68093
|
-
connection,
|
|
68094
|
-
authority: marginfiAccount.authority,
|
|
68095
|
-
tokens: [
|
|
68096
|
-
{ mint: repayOpts.repayBank.mint, tokenProgram: repayOpts.tokenProgram },
|
|
68097
|
-
{ mint: borrowOpts.borrowBank.mint, tokenProgram: borrowOpts.tokenProgram }
|
|
68098
|
-
]
|
|
68099
|
-
});
|
|
68100
|
-
const refreshIntegrationIxs = makeRefreshIntegrationBanksIxs(
|
|
68101
|
-
marginfiAccount,
|
|
68102
|
-
bankMap,
|
|
68103
|
-
[],
|
|
68104
|
-
bankMetadataMap,
|
|
68105
|
-
[repayOpts.repayBank.address, borrowOpts.borrowBank.address]
|
|
68106
|
-
);
|
|
68107
|
-
const { flashloanTx, setupInstructions, swapQuote, borrowIxs, repayIxs } = await buildSwapDebtFlashloanTx({
|
|
68108
|
-
...params,
|
|
68109
|
-
blockhash
|
|
68110
|
-
});
|
|
68111
|
-
const jupiterSetupInstructions = setupInstructions.filter((ix) => {
|
|
68112
|
-
if (ix.programId.equals(web3_js.ComputeBudgetProgram.programId)) {
|
|
68113
|
-
return false;
|
|
68114
|
-
}
|
|
68115
|
-
if (ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
|
|
68116
|
-
const mintKey = ix.keys[3]?.pubkey;
|
|
68117
|
-
if (mintKey?.equals(repayOpts.repayBank.mint) || mintKey?.equals(borrowOpts.borrowBank.mint)) {
|
|
68118
|
-
return false;
|
|
68119
|
-
}
|
|
68120
|
-
}
|
|
68121
|
-
return true;
|
|
68122
|
-
});
|
|
68123
|
-
setupIxs.push(...jupiterSetupInstructions);
|
|
68124
|
-
const { instructions: updateFeedIxs, luts: feedLuts } = await makeSmartCrankSwbFeedIx({
|
|
68125
|
-
marginfiAccount,
|
|
68126
|
-
bankMap,
|
|
68127
|
-
oraclePrices,
|
|
68128
|
-
assetShareValueMultiplierByBank,
|
|
68129
|
-
instructions: [...borrowIxs.instructions, ...repayIxs.instructions],
|
|
68130
|
-
program,
|
|
68131
|
-
connection,
|
|
68132
|
-
crossbarUrl
|
|
68133
|
-
});
|
|
68134
|
-
let additionalTxs = [];
|
|
68135
|
-
if (setupIxs.length > 0 || refreshIntegrationIxs.instructions.length > 0) {
|
|
68136
|
-
const ixs = [...additionalIxs, ...setupIxs, ...refreshIntegrationIxs.instructions];
|
|
68137
|
-
const txs = splitInstructionsToFitTransactions([], ixs, {
|
|
68138
|
-
blockhash,
|
|
68139
|
-
payerKey: marginfiAccount.authority,
|
|
68140
|
-
luts: addressLookupTableAccounts ?? []
|
|
68141
|
-
});
|
|
68142
|
-
additionalTxs.push(
|
|
68143
|
-
...txs.map(
|
|
68144
|
-
(tx) => addTransactionMetadata(tx, {
|
|
68145
|
-
type: "CREATE_ATA" /* CREATE_ATA */,
|
|
68146
|
-
addressLookupTables: addressLookupTableAccounts
|
|
68147
|
-
})
|
|
68148
|
-
)
|
|
68149
|
-
);
|
|
67440
|
+
// ----------------------------------------------------------------------------
|
|
67441
|
+
// Attributes
|
|
67442
|
+
// ----------------------------------------------------------------------------
|
|
67443
|
+
/**
|
|
67444
|
+
* Gets all balances that are currently active (non-zero).
|
|
67445
|
+
*
|
|
67446
|
+
* @returns Array of active Balance instances
|
|
67447
|
+
*/
|
|
67448
|
+
get activeBalances() {
|
|
67449
|
+
return this.balances.filter((b) => b.active);
|
|
68150
67450
|
}
|
|
68151
|
-
|
|
68152
|
-
|
|
68153
|
-
|
|
68154
|
-
|
|
68155
|
-
|
|
68156
|
-
|
|
68157
|
-
|
|
68158
|
-
|
|
68159
|
-
|
|
68160
|
-
type: "CRANK" /* CRANK */
|
|
68161
|
-
})
|
|
68162
|
-
);
|
|
67451
|
+
/**
|
|
67452
|
+
* Gets the balance for a specific bank.
|
|
67453
|
+
*
|
|
67454
|
+
* @param bankPk - The public key of the bank
|
|
67455
|
+
*
|
|
67456
|
+
* @returns The Balance instance for the bank (may be empty)
|
|
67457
|
+
*/
|
|
67458
|
+
getBalance(bankPk) {
|
|
67459
|
+
return Balance.fromBalanceType(getBalance(bankPk, this.balances));
|
|
68163
67460
|
}
|
|
68164
|
-
|
|
68165
|
-
|
|
68166
|
-
|
|
68167
|
-
|
|
68168
|
-
|
|
68169
|
-
|
|
68170
|
-
|
|
68171
|
-
async function buildSwapDebtFlashloanTx({
|
|
68172
|
-
program,
|
|
68173
|
-
marginfiAccount,
|
|
68174
|
-
bankMap,
|
|
68175
|
-
repayOpts,
|
|
68176
|
-
borrowOpts,
|
|
68177
|
-
swapOpts,
|
|
68178
|
-
bankMetadataMap,
|
|
68179
|
-
addressLookupTableAccounts,
|
|
68180
|
-
connection,
|
|
68181
|
-
overrideInferAccounts,
|
|
68182
|
-
blockhash,
|
|
68183
|
-
swapEngineRunner
|
|
68184
|
-
}) {
|
|
68185
|
-
const {
|
|
68186
|
-
repayBank,
|
|
68187
|
-
tokenProgram: repayTokenProgram,
|
|
68188
|
-
totalPositionAmount,
|
|
68189
|
-
repayAmount
|
|
68190
|
-
} = repayOpts;
|
|
68191
|
-
const { borrowBank, tokenProgram: borrowTokenProgram } = borrowOpts;
|
|
68192
|
-
if (repayAmount !== void 0 && repayAmount <= 0) {
|
|
68193
|
-
throw new Error("repayAmount must be greater than 0");
|
|
67461
|
+
/**
|
|
67462
|
+
* Checks if the account is disabled.
|
|
67463
|
+
*
|
|
67464
|
+
* @returns True if the account is disabled, false otherwise
|
|
67465
|
+
*/
|
|
67466
|
+
get isDisabled() {
|
|
67467
|
+
return this.accountFlags.includes(1 /* ACCOUNT_DISABLED */);
|
|
68194
67468
|
}
|
|
68195
|
-
|
|
68196
|
-
|
|
68197
|
-
|
|
68198
|
-
|
|
68199
|
-
|
|
68200
|
-
|
|
68201
|
-
|
|
68202
|
-
marginfiAccount.authority,
|
|
68203
|
-
true,
|
|
68204
|
-
repayTokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
|
|
68205
|
-
);
|
|
68206
|
-
const estimatedBorrowAmount = computeBorrowEstimateForRepay({
|
|
68207
|
-
repayTargetUi: actualRepayAmount,
|
|
68208
|
-
repayMarketPrice: repayOpts.marketPrice,
|
|
68209
|
-
borrowMarketPrice: borrowOpts.marketPrice,
|
|
68210
|
-
slippageBps: swapOpts.swapConfig?.slippageBps,
|
|
68211
|
-
isRepayAll: actualRepayAmount >= totalPositionAmount
|
|
68212
|
-
});
|
|
68213
|
-
const swapConstraints = await computeFlashloanSwapConstraints({
|
|
68214
|
-
program,
|
|
68215
|
-
marginfiAccount,
|
|
68216
|
-
bankMap,
|
|
68217
|
-
bankMetadataMap,
|
|
68218
|
-
addressLookupTableAccounts: addressLookupTableAccounts ?? [],
|
|
68219
|
-
primaryIx: { type: "borrow", bank: borrowBank, tokenProgram: borrowTokenProgram },
|
|
68220
|
-
secondaryIx: { type: "repay", bank: repayBank, tokenProgram: repayTokenProgram },
|
|
68221
|
-
overrideInferAccounts
|
|
68222
|
-
});
|
|
68223
|
-
const footprintBorrowIxs = await makeBorrowIx3({
|
|
68224
|
-
program,
|
|
68225
|
-
bank: borrowBank,
|
|
68226
|
-
bankMap,
|
|
68227
|
-
tokenProgram: borrowTokenProgram,
|
|
68228
|
-
amount: estimatedBorrowAmount,
|
|
68229
|
-
marginfiAccount,
|
|
68230
|
-
authority: marginfiAccount.authority,
|
|
68231
|
-
isSync: true,
|
|
68232
|
-
opts: { createAtas: false, wrapAndUnwrapSol: false, overrideInferAccounts }
|
|
68233
|
-
});
|
|
68234
|
-
const footprintRepayIxs = await makeRepayIx3({
|
|
68235
|
-
program,
|
|
68236
|
-
bank: repayBank,
|
|
68237
|
-
tokenProgram: repayTokenProgram,
|
|
68238
|
-
amount: actualRepayAmount,
|
|
68239
|
-
accountAddress: marginfiAccount.address,
|
|
68240
|
-
authority: marginfiAccount.authority,
|
|
68241
|
-
isSync: true,
|
|
68242
|
-
opts: { wrapAndUnwrapSol: false, overrideInferAccounts }
|
|
68243
|
-
});
|
|
68244
|
-
const runEngine = swapEngineRunner ?? runSwapEngine;
|
|
68245
|
-
const engineResult = await runEngine({
|
|
68246
|
-
inputMint: borrowBank.mint.toBase58(),
|
|
68247
|
-
outputMint: repayBank.mint.toBase58(),
|
|
68248
|
-
amountNative: uiToNative(estimatedBorrowAmount, borrowBank.mintDecimals).toNumber(),
|
|
68249
|
-
inputDecimals: borrowBank.mintDecimals,
|
|
68250
|
-
outputDecimals: repayBank.mintDecimals,
|
|
68251
|
-
...swapEngineQuoteFieldsFromOpts(swapOpts),
|
|
68252
|
-
taker: marginfiAccount.authority,
|
|
68253
|
-
destinationTokenAccount,
|
|
68254
|
-
connection,
|
|
68255
|
-
footprint: {
|
|
68256
|
-
instructions: [
|
|
68257
|
-
...cuRequestIxs,
|
|
68258
|
-
...footprintBorrowIxs.instructions,
|
|
68259
|
-
...footprintRepayIxs.instructions
|
|
68260
|
-
],
|
|
68261
|
-
luts: addressLookupTableAccounts ?? [],
|
|
68262
|
-
payer: marginfiAccount.authority,
|
|
68263
|
-
sizeConstraint: swapConstraints.sizeConstraint,
|
|
68264
|
-
maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
|
|
68265
|
-
},
|
|
68266
|
-
providers: swapEngineProvidersFromOpts(swapOpts)
|
|
68267
|
-
});
|
|
68268
|
-
const quoteResponse = engineResult.quoteResponse;
|
|
68269
|
-
const outAmount = nativeToUi(quoteResponse.outAmount, repayBank.mintDecimals);
|
|
68270
|
-
const outAmountThreshold = nativeToUi(quoteResponse.otherAmountThreshold, repayBank.mintDecimals);
|
|
68271
|
-
const amountToRepay = outAmount > totalPositionAmount ? totalPositionAmount : outAmountThreshold;
|
|
68272
|
-
const borrowAmount = nativeToUi(quoteResponse.inAmount, borrowBank.mintDecimals);
|
|
68273
|
-
const borrowIxs = await makeBorrowIx3({
|
|
68274
|
-
program,
|
|
68275
|
-
bank: borrowBank,
|
|
68276
|
-
bankMap,
|
|
68277
|
-
tokenProgram: borrowTokenProgram,
|
|
68278
|
-
amount: borrowAmount,
|
|
68279
|
-
marginfiAccount,
|
|
68280
|
-
authority: marginfiAccount.authority,
|
|
68281
|
-
isSync: true,
|
|
68282
|
-
opts: {
|
|
68283
|
-
createAtas: false,
|
|
68284
|
-
wrapAndUnwrapSol: false,
|
|
68285
|
-
overrideInferAccounts
|
|
68286
|
-
}
|
|
68287
|
-
});
|
|
68288
|
-
const repayIxs = await makeRepayIx3({
|
|
68289
|
-
program,
|
|
68290
|
-
bank: repayBank,
|
|
68291
|
-
tokenProgram: repayTokenProgram,
|
|
68292
|
-
amount: amountToRepay,
|
|
68293
|
-
accountAddress: marginfiAccount.address,
|
|
68294
|
-
authority: marginfiAccount.authority,
|
|
68295
|
-
repayAll: isWholePosition(
|
|
68296
|
-
{
|
|
68297
|
-
amount: totalPositionAmount,
|
|
68298
|
-
isLending: false
|
|
68299
|
-
},
|
|
68300
|
-
amountToRepay,
|
|
68301
|
-
repayBank.mintDecimals
|
|
68302
|
-
),
|
|
68303
|
-
isSync: true,
|
|
68304
|
-
opts: {
|
|
68305
|
-
wrapAndUnwrapSol: false,
|
|
68306
|
-
overrideInferAccounts
|
|
68307
|
-
}
|
|
68308
|
-
});
|
|
68309
|
-
const luts = [...addressLookupTableAccounts ?? [], ...engineResult.swapLuts];
|
|
68310
|
-
const allNonFlIxs = [
|
|
68311
|
-
...cuRequestIxs,
|
|
68312
|
-
...borrowIxs.instructions,
|
|
68313
|
-
...engineResult.swapInstructions,
|
|
68314
|
-
...repayIxs.instructions
|
|
68315
|
-
];
|
|
68316
|
-
compileFlashloanPrecheck({
|
|
68317
|
-
allIxs: allNonFlIxs,
|
|
68318
|
-
payer: marginfiAccount.authority,
|
|
68319
|
-
luts,
|
|
68320
|
-
sizeConstraint: swapConstraints.sizeConstraint,
|
|
68321
|
-
swapIxCount: engineResult.swapInstructions.length,
|
|
68322
|
-
swapLutCount: engineResult.swapLuts.length
|
|
68323
|
-
});
|
|
68324
|
-
const flashloanTx = await makeFlashLoanTx({
|
|
68325
|
-
program,
|
|
68326
|
-
marginfiAccount,
|
|
68327
|
-
bankMap,
|
|
68328
|
-
addressLookupTableAccounts: luts,
|
|
68329
|
-
blockhash,
|
|
68330
|
-
ixs: allNonFlIxs,
|
|
68331
|
-
isSync: true
|
|
68332
|
-
});
|
|
68333
|
-
const txSize = getTxSize(flashloanTx);
|
|
68334
|
-
const totalKeys = getTotalAccountKeys(flashloanTx);
|
|
68335
|
-
if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
|
|
68336
|
-
throw TransactionBuildingError.swapSizeExceededPositionSwap(
|
|
68337
|
-
txSize,
|
|
68338
|
-
totalKeys,
|
|
68339
|
-
swapOpts.swapConfig?.provider
|
|
68340
|
-
);
|
|
67469
|
+
/**
|
|
67470
|
+
* Checks if the account is currently in a flash loan.
|
|
67471
|
+
*
|
|
67472
|
+
* @returns True if a flash loan is active, false otherwise
|
|
67473
|
+
*/
|
|
67474
|
+
get isFlashLoanEnabled() {
|
|
67475
|
+
return this.accountFlags.includes(2 /* ACCOUNT_IN_FLASHLOAN */);
|
|
68341
67476
|
}
|
|
68342
|
-
|
|
68343
|
-
|
|
68344
|
-
|
|
68345
|
-
|
|
68346
|
-
|
|
68347
|
-
|
|
68348
|
-
|
|
68349
|
-
}
|
|
68350
|
-
var DEFAULT_ROLL_SLIPPAGE_BPS = 50;
|
|
68351
|
-
var TRADE_PT_EVENT_AMOUNT_OUT_OFFSET = 138;
|
|
68352
|
-
var MERGE_EVENT_AMOUNT_SY_OUT_OFFSET = 296;
|
|
68353
|
-
async function makeRollPtTx(params) {
|
|
68354
|
-
const {
|
|
68355
|
-
program,
|
|
68356
|
-
marginfiAccount,
|
|
68357
|
-
connection,
|
|
68358
|
-
bankMap,
|
|
68359
|
-
oraclePrices,
|
|
68360
|
-
withdrawOpts,
|
|
68361
|
-
depositOpts,
|
|
68362
|
-
rollOpts,
|
|
68363
|
-
assetShareValueMultiplierByBank,
|
|
68364
|
-
addressLookupTableAccounts,
|
|
68365
|
-
crossbarUrl
|
|
68366
|
-
} = params;
|
|
68367
|
-
if (!rollOpts.maturedMarket && !rollOpts.maturedVault) {
|
|
68368
|
-
throw new Error("roll-pt: rollOpts.maturedMarket or maturedVault is required");
|
|
67477
|
+
/**
|
|
67478
|
+
* Checks if account authority transfer is enabled.
|
|
67479
|
+
*
|
|
67480
|
+
* @returns True if authority transfer is allowed, false otherwise
|
|
67481
|
+
*/
|
|
67482
|
+
get isTransferAccountAuthorityEnabled() {
|
|
67483
|
+
return this.accountFlags.includes(8 /* ACCOUNT_TRANSFER_AUTHORITY_ALLOWED */);
|
|
68369
67484
|
}
|
|
68370
|
-
|
|
68371
|
-
|
|
68372
|
-
|
|
68373
|
-
|
|
68374
|
-
|
|
68375
|
-
|
|
68376
|
-
|
|
68377
|
-
|
|
68378
|
-
|
|
68379
|
-
|
|
68380
|
-
owner: marginfiAccount.authority,
|
|
68381
|
-
market: rollOpts.successorMarket,
|
|
68382
|
-
ptTokenProgram: depositOpts.tokenProgram,
|
|
68383
|
-
syTokenProgram: rollOpts.syTokenProgram
|
|
68384
|
-
});
|
|
68385
|
-
const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
|
|
68386
|
-
const setupIxs = await makeSetupIx({
|
|
68387
|
-
connection,
|
|
68388
|
-
authority: marginfiAccount.authority,
|
|
68389
|
-
tokens: [
|
|
68390
|
-
{ mint: withdrawOpts.withdrawBank.mint, tokenProgram: withdrawOpts.tokenProgram },
|
|
68391
|
-
{ mint: merge.mergeAccounts.mintYt, tokenProgram: withdrawOpts.tokenProgram },
|
|
68392
|
-
{ mint: merge.underlying.mint, tokenProgram: merge.underlying.tokenProgram },
|
|
68393
|
-
{ mint: depositOpts.depositBank.mint, tokenProgram: depositOpts.tokenProgram }
|
|
68394
|
-
]
|
|
68395
|
-
});
|
|
68396
|
-
const { flashloanTx, swapQuote, withdrawIxs, depositIxs } = await buildRollPtFlashloanTx({
|
|
68397
|
-
params,
|
|
68398
|
-
merge,
|
|
68399
|
-
clmm,
|
|
68400
|
-
setupIxs,
|
|
68401
|
-
blockhash
|
|
68402
|
-
});
|
|
68403
|
-
const { instructions: updateFeedIxs, luts: feedLuts } = await makeSmartCrankSwbFeedIx({
|
|
68404
|
-
marginfiAccount,
|
|
68405
|
-
bankMap,
|
|
68406
|
-
oraclePrices,
|
|
68407
|
-
assetShareValueMultiplierByBank,
|
|
68408
|
-
instructions: [...withdrawIxs.instructions, ...depositIxs.instructions],
|
|
68409
|
-
program,
|
|
68410
|
-
connection,
|
|
68411
|
-
crossbarUrl
|
|
68412
|
-
});
|
|
68413
|
-
const additionalTxs = [];
|
|
68414
|
-
if (setupIxs.length > 0) {
|
|
68415
|
-
const txs = splitInstructionsToFitTransactions([], setupIxs, {
|
|
68416
|
-
blockhash,
|
|
68417
|
-
payerKey: marginfiAccount.authority,
|
|
68418
|
-
luts: addressLookupTableAccounts ?? []
|
|
68419
|
-
});
|
|
68420
|
-
additionalTxs.push(
|
|
68421
|
-
...txs.map(
|
|
68422
|
-
(tx) => addTransactionMetadata(tx, {
|
|
68423
|
-
type: "CREATE_ATA" /* CREATE_ATA */,
|
|
68424
|
-
addressLookupTables: addressLookupTableAccounts
|
|
68425
|
-
})
|
|
68426
|
-
)
|
|
68427
|
-
);
|
|
67485
|
+
/**
|
|
67486
|
+
* Sets the health cache for this account.
|
|
67487
|
+
*
|
|
67488
|
+
* Note: This mutates the account instance. Consider using simulateHealthCache()
|
|
67489
|
+
* for a pure functional approach that returns a new account instance.
|
|
67490
|
+
*
|
|
67491
|
+
* @param value - The new health cache to set
|
|
67492
|
+
*/
|
|
67493
|
+
setHealthCache(value) {
|
|
67494
|
+
this.healthCache = value;
|
|
68428
67495
|
}
|
|
68429
|
-
|
|
68430
|
-
|
|
68431
|
-
|
|
68432
|
-
|
|
68433
|
-
|
|
68434
|
-
|
|
68435
|
-
|
|
68436
|
-
|
|
68437
|
-
|
|
68438
|
-
|
|
68439
|
-
|
|
68440
|
-
);
|
|
67496
|
+
/**
|
|
67497
|
+
* Computes free collateral using cached health values.
|
|
67498
|
+
*
|
|
67499
|
+
* Free collateral represents the amount of collateral that is not backing any liabilities.
|
|
67500
|
+
*
|
|
67501
|
+
* @param opts - Optional configuration
|
|
67502
|
+
* @param opts.clamped - If true, clamps negative values to zero
|
|
67503
|
+
*
|
|
67504
|
+
* @returns The free collateral amount in USD
|
|
67505
|
+
*/
|
|
67506
|
+
computeFreeCollateralFromCache(opts) {
|
|
67507
|
+
return computeFreeCollateralFromCache(this, opts);
|
|
68441
67508
|
}
|
|
68442
|
-
|
|
68443
|
-
|
|
68444
|
-
|
|
68445
|
-
|
|
68446
|
-
|
|
68447
|
-
|
|
68448
|
-
|
|
68449
|
-
|
|
68450
|
-
|
|
68451
|
-
|
|
68452
|
-
|
|
68453
|
-
|
|
68454
|
-
|
|
68455
|
-
})
|
|
68456
|
-
const {
|
|
68457
|
-
program,
|
|
68458
|
-
marginfiAccount,
|
|
68459
|
-
bankMap,
|
|
68460
|
-
withdrawOpts,
|
|
68461
|
-
depositOpts,
|
|
68462
|
-
bankMetadataMap,
|
|
68463
|
-
connection,
|
|
68464
|
-
addressLookupTableAccounts,
|
|
68465
|
-
overrideInferAccounts,
|
|
68466
|
-
rollOpts
|
|
68467
|
-
} = params;
|
|
68468
|
-
const { withdrawBank, tokenProgram: withdrawTokenProgram, totalPositionAmount, withdrawAmount } = withdrawOpts;
|
|
68469
|
-
const { depositBank, tokenProgram: depositTokenProgram } = depositOpts;
|
|
68470
|
-
const authority = marginfiAccount.authority;
|
|
68471
|
-
if (withdrawAmount !== void 0 && withdrawAmount <= 0) {
|
|
68472
|
-
throw new Error("withdrawAmount must be greater than 0");
|
|
67509
|
+
/**
|
|
67510
|
+
* Computes free collateral from balances using Initial margin requirements.
|
|
67511
|
+
*
|
|
67512
|
+
* Free collateral represents the amount of value available for new borrows or withdrawals.
|
|
67513
|
+
* By default, negative values are clamped to zero.
|
|
67514
|
+
*
|
|
67515
|
+
* @param params - Configuration for free collateral computation (excluding activeBalances)
|
|
67516
|
+
* @returns Free collateral value in USD (clamped to zero by default)
|
|
67517
|
+
*/
|
|
67518
|
+
computeFreeCollateralFromBalances(params) {
|
|
67519
|
+
return computeFreeCollateralFromBalances({
|
|
67520
|
+
activeBalances: this.activeBalances,
|
|
67521
|
+
...params
|
|
67522
|
+
});
|
|
68473
67523
|
}
|
|
68474
|
-
|
|
68475
|
-
|
|
68476
|
-
|
|
68477
|
-
|
|
68478
|
-
|
|
68479
|
-
|
|
68480
|
-
|
|
68481
|
-
|
|
68482
|
-
|
|
68483
|
-
|
|
68484
|
-
|
|
68485
|
-
web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
|
|
68486
|
-
];
|
|
68487
|
-
const withdrawIxs = await makeWithdrawIx3({
|
|
68488
|
-
program,
|
|
68489
|
-
bank: withdrawBank,
|
|
68490
|
-
bankMap,
|
|
68491
|
-
tokenProgram: withdrawTokenProgram,
|
|
68492
|
-
amount: actualWithdrawAmount,
|
|
68493
|
-
marginfiAccount,
|
|
68494
|
-
authority,
|
|
68495
|
-
withdrawAll: isFullWithdraw,
|
|
68496
|
-
isSync: false,
|
|
68497
|
-
opts: { createAtas: false, wrapAndUnwrapSol: false, overrideInferAccounts }
|
|
68498
|
-
});
|
|
68499
|
-
const mergeIx = makeExponentMergeIx(merge.mergeAccounts, withdrawNative);
|
|
68500
|
-
const depositIxs = await makeDepositIx3({
|
|
68501
|
-
program,
|
|
68502
|
-
bank: depositBank,
|
|
68503
|
-
tokenProgram: depositTokenProgram,
|
|
68504
|
-
amount: 0,
|
|
68505
|
-
accountAddress: marginfiAccount.address,
|
|
68506
|
-
authority,
|
|
68507
|
-
group: marginfiAccount.group,
|
|
68508
|
-
opts: { wrapAndUnwrapSol: false, overrideInferAccounts }
|
|
68509
|
-
});
|
|
68510
|
-
let luts;
|
|
68511
|
-
if (rollOpts.lookupTable) {
|
|
68512
|
-
const fetched = (await connection.getAddressLookupTable(rollOpts.lookupTable)).value;
|
|
68513
|
-
if (!fetched) {
|
|
68514
|
-
throw new Error(`roll-pt: PT-roll lookup table not found: ${rollOpts.lookupTable.toBase58()}`);
|
|
68515
|
-
}
|
|
68516
|
-
luts = [fetched, merge.addressLookupTable, clmm.addressLookupTable];
|
|
68517
|
-
} else {
|
|
68518
|
-
luts = [
|
|
68519
|
-
...addressLookupTableAccounts ?? [],
|
|
68520
|
-
merge.addressLookupTable,
|
|
68521
|
-
clmm.addressLookupTable
|
|
68522
|
-
];
|
|
67524
|
+
/**
|
|
67525
|
+
* Computes health components using cached health values.
|
|
67526
|
+
*
|
|
67527
|
+
* Returns weighted asset and liability values based on the margin requirement type.
|
|
67528
|
+
*
|
|
67529
|
+
* @param marginReqType - The margin requirement type (Initial, Maintenance, or Equity)
|
|
67530
|
+
*
|
|
67531
|
+
* @returns Object containing assets and liabilities values in USD
|
|
67532
|
+
*/
|
|
67533
|
+
computeHealthComponentsFromCache(marginRequirement) {
|
|
67534
|
+
return computeHealthComponentsFromCache(this, marginRequirement);
|
|
68523
67535
|
}
|
|
68524
|
-
|
|
68525
|
-
|
|
68526
|
-
|
|
68527
|
-
|
|
68528
|
-
|
|
68529
|
-
|
|
68530
|
-
|
|
68531
|
-
|
|
68532
|
-
|
|
68533
|
-
|
|
68534
|
-
|
|
68535
|
-
|
|
68536
|
-
|
|
68537
|
-
|
|
68538
|
-
throw new Error("roll-pt: merge would redeem 0 SY (empty/invalid matured vault state)");
|
|
68539
|
-
}
|
|
68540
|
-
const exactPtOut = await quoteClmmTradeOut({
|
|
68541
|
-
connection,
|
|
68542
|
-
clmm,
|
|
68543
|
-
amountInSyNative: syExact,
|
|
68544
|
-
payer: authority
|
|
68545
|
-
});
|
|
68546
|
-
const slippageBps = rollOpts.slippageBps ?? DEFAULT_ROLL_SLIPPAGE_BPS;
|
|
68547
|
-
const minPtOut = exactPtOut * BigInt(1e4 - slippageBps) / 10000n;
|
|
68548
|
-
if (minPtOut <= 0n) {
|
|
68549
|
-
throw new Error("roll-pt: quoted PT out is 0 (insufficient CLMM liquidity for this size)");
|
|
68550
|
-
}
|
|
68551
|
-
const tradeIx = makeExponentClmmTradePtIx(
|
|
68552
|
-
clmm.tradePtAccounts,
|
|
68553
|
-
exponentClmmBuyPtArgs({ amountInSyNative: syExact, minPtOutNative: minPtOut })
|
|
68554
|
-
);
|
|
68555
|
-
const depositIxToPatch = depositIxs.instructions.find(isDepositIx);
|
|
68556
|
-
if (!depositIxToPatch) {
|
|
68557
|
-
throw new Error("roll-pt: could not locate deposit instruction for amount patching");
|
|
68558
|
-
}
|
|
68559
|
-
patchDepositAmount(depositIxToPatch, new BN9__default.default(minPtOut.toString()));
|
|
68560
|
-
const allNonFlIxs = [
|
|
68561
|
-
...cuRequestIxs,
|
|
68562
|
-
...withdrawIxs.instructions,
|
|
68563
|
-
mergeIx,
|
|
68564
|
-
tradeIx,
|
|
68565
|
-
...depositIxs.instructions
|
|
68566
|
-
];
|
|
68567
|
-
const { sizeConstraint } = computeFlashLoanNonSwapBudget({
|
|
68568
|
-
program,
|
|
68569
|
-
marginfiAccount,
|
|
68570
|
-
bankMap,
|
|
68571
|
-
addressLookupTableAccounts: luts,
|
|
68572
|
-
ixs: allNonFlIxs
|
|
68573
|
-
});
|
|
68574
|
-
compileFlashloanPrecheck({
|
|
68575
|
-
allIxs: allNonFlIxs,
|
|
68576
|
-
payer: authority,
|
|
68577
|
-
luts,
|
|
68578
|
-
sizeConstraint,
|
|
68579
|
-
swapIxCount: 0,
|
|
68580
|
-
swapLutCount: 0
|
|
68581
|
-
});
|
|
68582
|
-
const flashloanTx = await makeFlashLoanTx({
|
|
68583
|
-
program,
|
|
68584
|
-
marginfiAccount,
|
|
68585
|
-
bankMap,
|
|
68586
|
-
addressLookupTableAccounts: luts,
|
|
68587
|
-
blockhash,
|
|
68588
|
-
ixs: allNonFlIxs,
|
|
68589
|
-
isSync: false
|
|
68590
|
-
});
|
|
68591
|
-
const txSize = getTxSize(flashloanTx);
|
|
68592
|
-
const totalKeys = getTotalAccountKeys(flashloanTx);
|
|
68593
|
-
if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
|
|
68594
|
-
throw TransactionBuildingError.swapSizeExceededPositionSwap(txSize, totalKeys, void 0);
|
|
68595
|
-
}
|
|
68596
|
-
const swapQuote = {
|
|
68597
|
-
inAmount: syExact.toString(),
|
|
68598
|
-
outAmount: exactPtOut.toString(),
|
|
68599
|
-
otherAmountThreshold: minPtOut.toString(),
|
|
68600
|
-
slippageBps
|
|
68601
|
-
};
|
|
68602
|
-
return { flashloanTx, swapQuote, withdrawIxs, depositIxs };
|
|
68603
|
-
}
|
|
68604
|
-
async function simulateRollReturn({
|
|
68605
|
-
program,
|
|
68606
|
-
marginfiAccount,
|
|
68607
|
-
bankMap,
|
|
68608
|
-
connection,
|
|
68609
|
-
blockhash,
|
|
68610
|
-
luts,
|
|
68611
|
-
ixs,
|
|
68612
|
-
programId,
|
|
68613
|
-
label
|
|
68614
|
-
}) {
|
|
68615
|
-
const quoteTx = await makeFlashLoanTx({
|
|
68616
|
-
program,
|
|
68617
|
-
marginfiAccount,
|
|
68618
|
-
bankMap,
|
|
68619
|
-
addressLookupTableAccounts: luts,
|
|
68620
|
-
blockhash,
|
|
68621
|
-
ixs,
|
|
68622
|
-
isSync: false
|
|
68623
|
-
});
|
|
68624
|
-
const sim = await connection.simulateTransaction(quoteTx, {
|
|
68625
|
-
sigVerify: false,
|
|
68626
|
-
replaceRecentBlockhash: true
|
|
68627
|
-
});
|
|
68628
|
-
const logs = sim.value.logs ?? [];
|
|
68629
|
-
if (process.env.ROLL_DEBUG) {
|
|
68630
|
-
console.error(
|
|
68631
|
-
`[roll ${label} sim] err:`,
|
|
68632
|
-
JSON.stringify(sim.value.err),
|
|
68633
|
-
"returnData:",
|
|
68634
|
-
JSON.stringify(sim.value.returnData),
|
|
68635
|
-
"logsLen:",
|
|
68636
|
-
logs.length,
|
|
68637
|
-
"truncated:",
|
|
68638
|
-
logs.some((l) => l.includes("truncated"))
|
|
68639
|
-
);
|
|
68640
|
-
}
|
|
68641
|
-
const prefix = `Program return: ${programId.toBase58()} `;
|
|
68642
|
-
const line = [...logs].reverse().find((l) => l.startsWith(prefix));
|
|
68643
|
-
if (!line) {
|
|
68644
|
-
throw new Error(
|
|
68645
|
-
`roll-pt: could not read ${label} return data from quote simulation (err=${JSON.stringify(
|
|
68646
|
-
sim.value.err
|
|
68647
|
-
)})`
|
|
68648
|
-
);
|
|
68649
|
-
}
|
|
68650
|
-
return Buffer.from(line.slice(prefix.length), "base64");
|
|
68651
|
-
}
|
|
68652
|
-
function readReturnU64(data, offset, what) {
|
|
68653
|
-
if (data.length < offset + 8) {
|
|
68654
|
-
throw new Error(`roll-pt: ${what} return data too short (${data.length} bytes)`);
|
|
68655
|
-
}
|
|
68656
|
-
return data.readBigUInt64LE(offset);
|
|
68657
|
-
}
|
|
68658
|
-
async function quoteClmmTradeOut({
|
|
68659
|
-
connection,
|
|
68660
|
-
clmm,
|
|
68661
|
-
amountInSyNative,
|
|
68662
|
-
payer
|
|
68663
|
-
}) {
|
|
68664
|
-
const excluded = /* @__PURE__ */ new Set([
|
|
68665
|
-
clmm.tradePtAccounts.tokenSyEscrow.toBase58(),
|
|
68666
|
-
clmm.tradePtAccounts.tokenFeeTreasurySy.toBase58()
|
|
68667
|
-
]);
|
|
68668
|
-
const largest = await connection.getTokenLargestAccounts(clmm.sy.mint);
|
|
68669
|
-
const funded = largest.value.find(
|
|
68670
|
-
(a) => !excluded.has(a.address.toBase58()) && BigInt(a.amount) >= amountInSyNative
|
|
68671
|
-
);
|
|
68672
|
-
if (!funded) {
|
|
68673
|
-
throw new Error(
|
|
68674
|
-
"roll-pt: no SY holder large enough to quote the buy \u2014 the roll size exceeds available CLMM liquidity for this pair"
|
|
68675
|
-
);
|
|
68676
|
-
}
|
|
68677
|
-
const parsed = await connection.getParsedAccountInfo(funded.address);
|
|
68678
|
-
const info = parsed.value?.data?.parsed?.info;
|
|
68679
|
-
if (!info?.owner) throw new Error("roll-pt: could not resolve the quote SY holder's owner");
|
|
68680
|
-
const trader = new web3_js.PublicKey(info.owner);
|
|
68681
|
-
const ptTokenProgram = clmm.pt.tokenProgram ?? TOKEN_PROGRAM_ID;
|
|
68682
|
-
const tokenPtTrader = getAssociatedTokenAddressSync(clmm.pt.mint, trader, true, ptTokenProgram);
|
|
68683
|
-
const quoteIx = makeExponentClmmTradePtIx(
|
|
68684
|
-
{ ...clmm.tradePtAccounts, trader, tokenSyTrader: funded.address, tokenPtTrader },
|
|
68685
|
-
exponentClmmBuyPtArgs({ amountInSyNative, minPtOutNative: 1n })
|
|
68686
|
-
);
|
|
68687
|
-
const createPtAta = createAssociatedTokenAccountIdempotentInstruction(
|
|
68688
|
-
payer,
|
|
68689
|
-
tokenPtTrader,
|
|
68690
|
-
trader,
|
|
68691
|
-
clmm.pt.mint,
|
|
68692
|
-
ptTokenProgram
|
|
68693
|
-
);
|
|
68694
|
-
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
68695
|
-
const message = new web3_js.TransactionMessage({
|
|
68696
|
-
payerKey: payer,
|
|
68697
|
-
recentBlockhash: blockhash,
|
|
68698
|
-
instructions: [createPtAta, quoteIx]
|
|
68699
|
-
}).compileToV0Message([clmm.addressLookupTable]);
|
|
68700
|
-
const sim = await connection.simulateTransaction(new web3_js.VersionedTransaction(message), {
|
|
68701
|
-
sigVerify: false,
|
|
68702
|
-
replaceRecentBlockhash: true
|
|
68703
|
-
});
|
|
68704
|
-
const rd = sim.value.returnData;
|
|
68705
|
-
if (process.env.ROLL_DEBUG) {
|
|
68706
|
-
console.error("[roll trade quote] err:", JSON.stringify(sim.value.err), "returnData?", !!rd);
|
|
68707
|
-
}
|
|
68708
|
-
if (!rd?.data || rd.programId !== EXPONENT_CLMM_PROGRAM_ID.toBase58()) {
|
|
68709
|
-
throw new Error(
|
|
68710
|
-
`roll-pt: CLMM trade quote produced no return data (err=${JSON.stringify(sim.value.err)})`
|
|
68711
|
-
);
|
|
68712
|
-
}
|
|
68713
|
-
const data = Buffer.from(rd.data[0], rd.data[1]);
|
|
68714
|
-
return readReturnU64(data, TRADE_PT_EVENT_AMOUNT_OUT_OFFSET, "trade_pt amount_out");
|
|
68715
|
-
}
|
|
68716
|
-
|
|
68717
|
-
// src/models/balance.ts
|
|
68718
|
-
var Balance = class _Balance {
|
|
68719
|
-
constructor(active, bankPk, assetShares, liabilityShares, emissionsOutstanding, lastUpdate) {
|
|
68720
|
-
this.active = active;
|
|
68721
|
-
this.bankPk = bankPk;
|
|
68722
|
-
this.assetShares = assetShares;
|
|
68723
|
-
this.liabilityShares = liabilityShares;
|
|
68724
|
-
this.emissionsOutstanding = emissionsOutstanding;
|
|
68725
|
-
this.lastUpdate = lastUpdate;
|
|
68726
|
-
}
|
|
68727
|
-
static from(balanceRaw) {
|
|
68728
|
-
const props = parseBalanceRaw(balanceRaw);
|
|
68729
|
-
return new _Balance(
|
|
68730
|
-
props.active,
|
|
68731
|
-
props.bankPk,
|
|
68732
|
-
props.assetShares,
|
|
68733
|
-
props.liabilityShares,
|
|
68734
|
-
props.emissionsOutstanding,
|
|
68735
|
-
props.lastUpdate
|
|
68736
|
-
);
|
|
68737
|
-
}
|
|
68738
|
-
static fromBalanceType(balance) {
|
|
68739
|
-
return new _Balance(
|
|
68740
|
-
balance.active,
|
|
68741
|
-
balance.bankPk,
|
|
68742
|
-
balance.assetShares,
|
|
68743
|
-
balance.liabilityShares,
|
|
68744
|
-
balance.emissionsOutstanding,
|
|
68745
|
-
balance.lastUpdate
|
|
68746
|
-
);
|
|
68747
|
-
}
|
|
68748
|
-
static createEmpty(bankPk) {
|
|
68749
|
-
const balance = createEmptyBalance(bankPk);
|
|
68750
|
-
return this.fromBalanceType(balance);
|
|
68751
|
-
}
|
|
68752
|
-
computeUsdValue(bank, oraclePrice, marginRequirement = 2 /* Equity */, assetShareValueMultiplier, activeEmodeWeights) {
|
|
68753
|
-
return computeBalanceUsdValue({
|
|
68754
|
-
balance: this,
|
|
68755
|
-
bank,
|
|
68756
|
-
oraclePrice,
|
|
68757
|
-
marginRequirement,
|
|
68758
|
-
assetShareValueMultiplier,
|
|
68759
|
-
activeEmodeWeights
|
|
68760
|
-
});
|
|
68761
|
-
}
|
|
68762
|
-
getUsdValueWithPriceBias(bank, oraclePrice, marginRequirement = 2 /* Equity */, assetShareValueMultiplier, activeEmodeWeights) {
|
|
68763
|
-
return getBalanceUsdValueWithPriceBias({
|
|
68764
|
-
balance: this,
|
|
68765
|
-
bank,
|
|
68766
|
-
oraclePrice,
|
|
68767
|
-
marginRequirement,
|
|
68768
|
-
assetShareValueMultiplier,
|
|
68769
|
-
activeEmodeWeights
|
|
68770
|
-
});
|
|
68771
|
-
}
|
|
68772
|
-
computeQuantity(bank) {
|
|
68773
|
-
return computeQuantity(this, bank);
|
|
68774
|
-
}
|
|
68775
|
-
computeQuantityUi(bank, assetShareValueMultiplier) {
|
|
68776
|
-
return computeQuantityUi(this, bank, assetShareValueMultiplier);
|
|
68777
|
-
}
|
|
68778
|
-
computeTotalOutstandingEmissions(bank) {
|
|
68779
|
-
return computeTotalOutstandingEmissions(this, bank);
|
|
68780
|
-
}
|
|
68781
|
-
computeClaimedEmissions(bank, currentTimestamp) {
|
|
68782
|
-
return computeClaimedEmissions(this, bank, currentTimestamp);
|
|
68783
|
-
}
|
|
68784
|
-
};
|
|
68785
|
-
|
|
68786
|
-
// src/models/health-cache.ts
|
|
68787
|
-
var HealthCache = class _HealthCache {
|
|
68788
|
-
constructor(assetValue, liabilityValue, assetValueMaint, liabilityValueMaint, assetValueEquity, liabilityValueEquity, timestamp, flags, prices, simulationStatus) {
|
|
68789
|
-
this.assetValue = assetValue;
|
|
68790
|
-
this.liabilityValue = liabilityValue;
|
|
68791
|
-
this.assetValueMaint = assetValueMaint;
|
|
68792
|
-
this.liabilityValueMaint = liabilityValueMaint;
|
|
68793
|
-
this.assetValueEquity = assetValueEquity;
|
|
68794
|
-
this.liabilityValueEquity = liabilityValueEquity;
|
|
68795
|
-
this.timestamp = timestamp;
|
|
68796
|
-
this.flags = flags;
|
|
68797
|
-
this.prices = prices;
|
|
68798
|
-
this.simulationStatus = simulationStatus;
|
|
68799
|
-
}
|
|
68800
|
-
static fromHealthCacheType(healthCacheType) {
|
|
68801
|
-
return new _HealthCache(
|
|
68802
|
-
healthCacheType.assetValue,
|
|
68803
|
-
healthCacheType.liabilityValue,
|
|
68804
|
-
healthCacheType.assetValueMaint,
|
|
68805
|
-
healthCacheType.liabilityValueMaint,
|
|
68806
|
-
healthCacheType.assetValueEquity,
|
|
68807
|
-
healthCacheType.liabilityValueEquity,
|
|
68808
|
-
healthCacheType.timestamp,
|
|
68809
|
-
healthCacheType.flags,
|
|
68810
|
-
healthCacheType.prices,
|
|
68811
|
-
healthCacheType.simulationStatus
|
|
68812
|
-
);
|
|
68813
|
-
}
|
|
68814
|
-
static from(healthCacheRaw) {
|
|
68815
|
-
return this.fromHealthCacheType(parseHealthCacheRaw(healthCacheRaw));
|
|
68816
|
-
}
|
|
68817
|
-
};
|
|
68818
|
-
|
|
68819
|
-
// src/models/account.ts
|
|
68820
|
-
var MarginfiAccount = class _MarginfiAccount {
|
|
68821
|
-
constructor(address, group, authority, balances, accountFlags, emissionsDestinationAccount, healthCache) {
|
|
68822
|
-
this.address = address;
|
|
68823
|
-
this.group = group;
|
|
68824
|
-
this.authority = authority;
|
|
68825
|
-
this.balances = balances;
|
|
68826
|
-
this.accountFlags = accountFlags;
|
|
68827
|
-
this.emissionsDestinationAccount = emissionsDestinationAccount;
|
|
68828
|
-
this.healthCache = healthCache;
|
|
68829
|
-
}
|
|
68830
|
-
/**
|
|
68831
|
-
* Fetches a marginfi account from on-chain data.
|
|
68832
|
-
*
|
|
68833
|
-
* @param address - The public key of the marginfi account
|
|
68834
|
-
* @param program - The Marginfi program instance
|
|
68835
|
-
*
|
|
68836
|
-
* @returns Promise resolving to a MarginfiAccount instance
|
|
68837
|
-
*/
|
|
68838
|
-
static async fetch(address, program) {
|
|
68839
|
-
const data = await program.account.marginfiAccount.fetch(address);
|
|
68840
|
-
return _MarginfiAccount.fromAccountParsed(address, data);
|
|
68841
|
-
}
|
|
68842
|
-
static decodeAccountRaw(encoded, idl) {
|
|
68843
|
-
return decodeAccountRaw(encoded, idl);
|
|
68844
|
-
}
|
|
68845
|
-
static fromAccountType(account) {
|
|
68846
|
-
return new _MarginfiAccount(
|
|
68847
|
-
account.address,
|
|
68848
|
-
account.group,
|
|
68849
|
-
account.authority,
|
|
68850
|
-
account.balances.map((b) => Balance.fromBalanceType(b)),
|
|
68851
|
-
account.accountFlags,
|
|
68852
|
-
account.emissionsDestinationAccount,
|
|
68853
|
-
account.healthCache
|
|
68854
|
-
);
|
|
68855
|
-
}
|
|
68856
|
-
/**
|
|
68857
|
-
* Creates a MarginfiAccount instance from parsed account data.
|
|
68858
|
-
*
|
|
68859
|
-
* @param marginfiAccountPk - The public key of the marginfi account
|
|
68860
|
-
* @param accountData - The raw account data from the program
|
|
68861
|
-
*
|
|
68862
|
-
* @returns A new MarginfiAccount instance
|
|
68863
|
-
*/
|
|
68864
|
-
static fromAccountParsed(marginfiAccountPk, accountData) {
|
|
68865
|
-
const props = parseMarginfiAccountRaw(marginfiAccountPk, accountData);
|
|
68866
|
-
return new _MarginfiAccount(
|
|
68867
|
-
props.address,
|
|
68868
|
-
props.group,
|
|
68869
|
-
props.authority,
|
|
68870
|
-
props.balances.map((b) => Balance.fromBalanceType(b)),
|
|
68871
|
-
props.accountFlags,
|
|
68872
|
-
props.emissionsDestinationAccount,
|
|
68873
|
-
HealthCache.fromHealthCacheType(props.healthCache)
|
|
68874
|
-
);
|
|
68875
|
-
}
|
|
68876
|
-
/**
|
|
68877
|
-
* Simulates and updates the health cache for this account.
|
|
68878
|
-
*
|
|
68879
|
-
* Fetches current oracle prices and computes fresh health values. Returns a new
|
|
68880
|
-
* account instance with updated health cache (does not mutate this instance).
|
|
68881
|
-
*
|
|
68882
|
-
* @param params - Configuration for health cache simulation (excluding marginfiAccount)
|
|
68883
|
-
* @returns Object containing the updated account and any errors
|
|
68884
|
-
*/
|
|
68885
|
-
async simulateHealthCache(params) {
|
|
68886
|
-
const accountWithHealthCache = await simulateAccountHealthCacheWithFallback({
|
|
68887
|
-
marginfiAccount: this,
|
|
68888
|
-
...params
|
|
68889
|
-
});
|
|
68890
|
-
return {
|
|
68891
|
-
account: _MarginfiAccount.fromAccountType(accountWithHealthCache.marginfiAccount),
|
|
68892
|
-
error: accountWithHealthCache.error
|
|
68893
|
-
};
|
|
68894
|
-
}
|
|
68895
|
-
static fromAccountDataRaw(marginfiAccountPk, rawData, idl) {
|
|
68896
|
-
const marginfiAccountData = _MarginfiAccount.decodeAccountRaw(rawData, idl);
|
|
68897
|
-
return _MarginfiAccount.fromAccountParsed(marginfiAccountPk, marginfiAccountData);
|
|
68898
|
-
}
|
|
68899
|
-
// ----------------------------------------------------------------------------
|
|
68900
|
-
// Attributes
|
|
68901
|
-
// ----------------------------------------------------------------------------
|
|
68902
|
-
/**
|
|
68903
|
-
* Gets all balances that are currently active (non-zero).
|
|
68904
|
-
*
|
|
68905
|
-
* @returns Array of active Balance instances
|
|
68906
|
-
*/
|
|
68907
|
-
get activeBalances() {
|
|
68908
|
-
return this.balances.filter((b) => b.active);
|
|
68909
|
-
}
|
|
68910
|
-
/**
|
|
68911
|
-
* Gets the balance for a specific bank.
|
|
68912
|
-
*
|
|
68913
|
-
* @param bankPk - The public key of the bank
|
|
68914
|
-
*
|
|
68915
|
-
* @returns The Balance instance for the bank (may be empty)
|
|
68916
|
-
*/
|
|
68917
|
-
getBalance(bankPk) {
|
|
68918
|
-
return Balance.fromBalanceType(getBalance(bankPk, this.balances));
|
|
68919
|
-
}
|
|
68920
|
-
/**
|
|
68921
|
-
* Checks if the account is disabled.
|
|
68922
|
-
*
|
|
68923
|
-
* @returns True if the account is disabled, false otherwise
|
|
68924
|
-
*/
|
|
68925
|
-
get isDisabled() {
|
|
68926
|
-
return this.accountFlags.includes(1 /* ACCOUNT_DISABLED */);
|
|
68927
|
-
}
|
|
68928
|
-
/**
|
|
68929
|
-
* Checks if the account is currently in a flash loan.
|
|
68930
|
-
*
|
|
68931
|
-
* @returns True if a flash loan is active, false otherwise
|
|
68932
|
-
*/
|
|
68933
|
-
get isFlashLoanEnabled() {
|
|
68934
|
-
return this.accountFlags.includes(2 /* ACCOUNT_IN_FLASHLOAN */);
|
|
68935
|
-
}
|
|
68936
|
-
/**
|
|
68937
|
-
* Checks if account authority transfer is enabled.
|
|
68938
|
-
*
|
|
68939
|
-
* @returns True if authority transfer is allowed, false otherwise
|
|
68940
|
-
*/
|
|
68941
|
-
get isTransferAccountAuthorityEnabled() {
|
|
68942
|
-
return this.accountFlags.includes(8 /* ACCOUNT_TRANSFER_AUTHORITY_ALLOWED */);
|
|
68943
|
-
}
|
|
68944
|
-
/**
|
|
68945
|
-
* Sets the health cache for this account.
|
|
68946
|
-
*
|
|
68947
|
-
* Note: This mutates the account instance. Consider using simulateHealthCache()
|
|
68948
|
-
* for a pure functional approach that returns a new account instance.
|
|
68949
|
-
*
|
|
68950
|
-
* @param value - The new health cache to set
|
|
68951
|
-
*/
|
|
68952
|
-
setHealthCache(value) {
|
|
68953
|
-
this.healthCache = value;
|
|
68954
|
-
}
|
|
68955
|
-
/**
|
|
68956
|
-
* Computes free collateral using cached health values.
|
|
68957
|
-
*
|
|
68958
|
-
* Free collateral represents the amount of collateral that is not backing any liabilities.
|
|
68959
|
-
*
|
|
68960
|
-
* @param opts - Optional configuration
|
|
68961
|
-
* @param opts.clamped - If true, clamps negative values to zero
|
|
68962
|
-
*
|
|
68963
|
-
* @returns The free collateral amount in USD
|
|
68964
|
-
*/
|
|
68965
|
-
computeFreeCollateralFromCache(opts) {
|
|
68966
|
-
return computeFreeCollateralFromCache(this, opts);
|
|
68967
|
-
}
|
|
68968
|
-
/**
|
|
68969
|
-
* Computes free collateral from balances using Initial margin requirements.
|
|
68970
|
-
*
|
|
68971
|
-
* Free collateral represents the amount of value available for new borrows or withdrawals.
|
|
68972
|
-
* By default, negative values are clamped to zero.
|
|
68973
|
-
*
|
|
68974
|
-
* @param params - Configuration for free collateral computation (excluding activeBalances)
|
|
68975
|
-
* @returns Free collateral value in USD (clamped to zero by default)
|
|
68976
|
-
*/
|
|
68977
|
-
computeFreeCollateralFromBalances(params) {
|
|
68978
|
-
return computeFreeCollateralFromBalances({
|
|
68979
|
-
activeBalances: this.activeBalances,
|
|
68980
|
-
...params
|
|
68981
|
-
});
|
|
68982
|
-
}
|
|
68983
|
-
/**
|
|
68984
|
-
* Computes health components using cached health values.
|
|
68985
|
-
*
|
|
68986
|
-
* Returns weighted asset and liability values based on the margin requirement type.
|
|
68987
|
-
*
|
|
68988
|
-
* @param marginReqType - The margin requirement type (Initial, Maintenance, or Equity)
|
|
68989
|
-
*
|
|
68990
|
-
* @returns Object containing assets and liabilities values in USD
|
|
68991
|
-
*/
|
|
68992
|
-
computeHealthComponentsFromCache(marginRequirement) {
|
|
68993
|
-
return computeHealthComponentsFromCache(this, marginRequirement);
|
|
68994
|
-
}
|
|
68995
|
-
/**
|
|
68996
|
-
* Computes health components from balances with price bias.
|
|
68997
|
-
*
|
|
68998
|
-
* Returns weighted asset and liability values using conservative pricing
|
|
68999
|
-
* (Lowest for assets, Highest for liabilities).
|
|
69000
|
-
*
|
|
69001
|
-
* @param params - Configuration for health computation (excluding activeBalances)
|
|
69002
|
-
* @returns Object containing assets and liabilities values in USD
|
|
69003
|
-
*/
|
|
69004
|
-
computeHealthComponentsFromBalances(params) {
|
|
69005
|
-
return computeHealthComponentsFromBalances({
|
|
69006
|
-
activeBalances: this.activeBalances,
|
|
69007
|
-
...params
|
|
69008
|
-
});
|
|
67536
|
+
/**
|
|
67537
|
+
* Computes health components from balances with price bias.
|
|
67538
|
+
*
|
|
67539
|
+
* Returns weighted asset and liability values using conservative pricing
|
|
67540
|
+
* (Lowest for assets, Highest for liabilities).
|
|
67541
|
+
*
|
|
67542
|
+
* @param params - Configuration for health computation (excluding activeBalances)
|
|
67543
|
+
* @returns Object containing assets and liabilities values in USD
|
|
67544
|
+
*/
|
|
67545
|
+
computeHealthComponentsFromBalances(params) {
|
|
67546
|
+
return computeHealthComponentsFromBalances({
|
|
67547
|
+
activeBalances: this.activeBalances,
|
|
67548
|
+
...params
|
|
67549
|
+
});
|
|
69009
67550
|
}
|
|
69010
67551
|
/**
|
|
69011
67552
|
* Computes the total account value (equity).
|
|
@@ -69410,6 +67951,86 @@ var MarginfiAccount = class _MarginfiAccount {
|
|
|
69410
67951
|
}
|
|
69411
67952
|
});
|
|
69412
67953
|
}
|
|
67954
|
+
/**
|
|
67955
|
+
* Creates a loop transaction with a transparent bridged (double-hop) fallback.
|
|
67956
|
+
*
|
|
67957
|
+
* One call: tries the direct {@link makeLoopTx} first; if its borrow→deposit swap can't fit one
|
|
67958
|
+
* transaction (size / account-locks) or has no route, it loops the deposit asset against a
|
|
67959
|
+
* value-equivalent borrow of a high-liquidity bridge token, then debt-swaps the bridge debt to
|
|
67960
|
+
* the requested borrow asset — both legs composed into ONE atomic Jito bundle.
|
|
67961
|
+
*
|
|
67962
|
+
* Bridge candidates default to USDC → wSOL → USDT and can be reordered/overridden via
|
|
67963
|
+
* `params.bridgeOpts.bridgeCandidateMints`; `bridgeOpts` also accepts known token programs (skips RPC
|
|
67964
|
+
* lookups), a bundle-size ceiling, and an abort signal. `result.bridgeMint` is set only when the
|
|
67965
|
+
* bridged path was used.
|
|
67966
|
+
*
|
|
67967
|
+
* Intended for existing accounts — a fresh account's loop fits the direct path, so flows that
|
|
67968
|
+
* create the account in the same action should call {@link makeLoopTx} directly.
|
|
67969
|
+
*
|
|
67970
|
+
* @param params - Loop transaction parameters plus optional `bridgeOpts`
|
|
67971
|
+
* @returns Object containing transactions, action index, merged swap quote, and the bridge mint
|
|
67972
|
+
*
|
|
67973
|
+
* @see {@link makeBridgedLoopTx} for detailed implementation
|
|
67974
|
+
*/
|
|
67975
|
+
async makeBridgedLoopTx(params) {
|
|
67976
|
+
return makeBridgedLoopTx({
|
|
67977
|
+
...params,
|
|
67978
|
+
marginfiAccount: this,
|
|
67979
|
+
overrideInferAccounts: {
|
|
67980
|
+
authority: this.authority,
|
|
67981
|
+
group: this.group,
|
|
67982
|
+
...params.overrideInferAccounts
|
|
67983
|
+
}
|
|
67984
|
+
});
|
|
67985
|
+
}
|
|
67986
|
+
/**
|
|
67987
|
+
* Creates a collateral-swap transaction with a transparent bridged (double-hop) fallback.
|
|
67988
|
+
*
|
|
67989
|
+
* One call: tries the direct {@link makeSwapCollateralTx} first; if the swap `A → C` can't fit
|
|
67990
|
+
* one transaction or has no route, it decomposes into `A → bridge` + `bridge → C` through a
|
|
67991
|
+
* high-liquidity bridge collateral, both legs composed into ONE atomic Jito bundle. See
|
|
67992
|
+
* {@link makeBridgedLoopTx} for the `bridgeOpts` knobs.
|
|
67993
|
+
*
|
|
67994
|
+
* @param params - Swap collateral transaction parameters plus optional `bridgeOpts`
|
|
67995
|
+
* @returns Object containing transactions, action index, merged swap quote, and the bridge mint
|
|
67996
|
+
*
|
|
67997
|
+
* @see {@link makeBridgedSwapCollateralTx} for detailed implementation
|
|
67998
|
+
*/
|
|
67999
|
+
async makeBridgedSwapCollateralTx(params) {
|
|
68000
|
+
return makeBridgedSwapCollateralTx({
|
|
68001
|
+
...params,
|
|
68002
|
+
marginfiAccount: this,
|
|
68003
|
+
overrideInferAccounts: {
|
|
68004
|
+
authority: this.authority,
|
|
68005
|
+
group: this.group,
|
|
68006
|
+
...params.overrideInferAccounts
|
|
68007
|
+
}
|
|
68008
|
+
});
|
|
68009
|
+
}
|
|
68010
|
+
/**
|
|
68011
|
+
* Creates a debt-swap transaction with a transparent bridged (double-hop) fallback.
|
|
68012
|
+
*
|
|
68013
|
+
* One call: tries the direct {@link makeSwapDebtTx} first; if the swap `A → C` can't fit one
|
|
68014
|
+
* transaction or has no route, the first leg repays A by borrowing a bridge token and the second
|
|
68015
|
+
* leg repays exactly that bridge debt while borrowing C — both legs composed into ONE atomic
|
|
68016
|
+
* Jito bundle. See {@link makeBridgedLoopTx} for the `bridgeOpts` knobs.
|
|
68017
|
+
*
|
|
68018
|
+
* @param params - Swap debt transaction parameters plus optional `bridgeOpts`
|
|
68019
|
+
* @returns Object containing transactions, action index, merged swap quote, and the bridge mint
|
|
68020
|
+
*
|
|
68021
|
+
* @see {@link makeBridgedSwapDebtTx} for detailed implementation
|
|
68022
|
+
*/
|
|
68023
|
+
async makeBridgedSwapDebtTx(params) {
|
|
68024
|
+
return makeBridgedSwapDebtTx({
|
|
68025
|
+
...params,
|
|
68026
|
+
marginfiAccount: this,
|
|
68027
|
+
overrideInferAccounts: {
|
|
68028
|
+
authority: this.authority,
|
|
68029
|
+
group: this.group,
|
|
68030
|
+
...params.overrideInferAccounts
|
|
68031
|
+
}
|
|
68032
|
+
});
|
|
68033
|
+
}
|
|
69413
68034
|
/**
|
|
69414
68035
|
* Creates a transaction to repay debt using collateral.
|
|
69415
68036
|
*
|
|
@@ -69449,425 +68070,2168 @@ var MarginfiAccount = class _MarginfiAccount {
|
|
|
69449
68070
|
}
|
|
69450
68071
|
});
|
|
69451
68072
|
}
|
|
69452
|
-
/**
|
|
69453
|
-
* Creates a transaction to swap one collateral position to another using a flash loan.
|
|
69454
|
-
*
|
|
69455
|
-
* A swap collateral transaction:
|
|
69456
|
-
* 1. Withdraws existing collateral via flash loan
|
|
69457
|
-
* 2. Swaps collateral to new asset (via Jupiter)
|
|
69458
|
-
* 3. Deposits swapped assets as new collateral
|
|
69459
|
-
*
|
|
69460
|
-
* This allows users to change their collateral type (e.g., JitoSOL -> mSOL) without
|
|
69461
|
-
* withdrawing and affecting their health during the swap.
|
|
69462
|
-
*
|
|
69463
|
-
* @param params - Swap collateral transaction parameters
|
|
69464
|
-
* @param params.connection - Solana connection instance
|
|
69465
|
-
* @param params.oraclePrices - Map of current oracle prices
|
|
69466
|
-
* @param params.withdrawOpts - Withdraw configuration (bank, amount, tokenProgram)
|
|
69467
|
-
* @param params.depositOpts - Deposit configuration (bank, tokenProgram)
|
|
69468
|
-
* @param params.swapOpts - Swap configuration (venue, slippage, fees)
|
|
69469
|
-
* @param params.addressLookupTableAccounts - Address lookup tables
|
|
69470
|
-
* @param params.overrideInferAccounts - Optional account overrides
|
|
69471
|
-
* @param params.additionalIxs - Additional instructions to include
|
|
69472
|
-
* @param params.crossbarUrl - Crossbar URL for oracle updates
|
|
69473
|
-
*
|
|
69474
|
-
* @returns Object containing transactions array, action index, and swap quote
|
|
69475
|
-
*
|
|
69476
|
-
* @throws {TransactionBuildingError} If swap exceeds transaction size limits
|
|
69477
|
-
* @throws {TransactionBuildingError} If Kamino reserve not found
|
|
69478
|
-
*
|
|
69479
|
-
* @see {@link makeSwapCollateralTx} for detailed implementation
|
|
69480
|
-
*/
|
|
69481
|
-
async makeSwapCollateralTx(params) {
|
|
69482
|
-
return makeSwapCollateralTx({
|
|
69483
|
-
...params,
|
|
69484
|
-
marginfiAccount: this,
|
|
69485
|
-
overrideInferAccounts: {
|
|
69486
|
-
authority: this.authority,
|
|
69487
|
-
group: this.group,
|
|
69488
|
-
...params.overrideInferAccounts
|
|
69489
|
-
}
|
|
68073
|
+
/**
|
|
68074
|
+
* Creates a transaction to swap one collateral position to another using a flash loan.
|
|
68075
|
+
*
|
|
68076
|
+
* A swap collateral transaction:
|
|
68077
|
+
* 1. Withdraws existing collateral via flash loan
|
|
68078
|
+
* 2. Swaps collateral to new asset (via Jupiter)
|
|
68079
|
+
* 3. Deposits swapped assets as new collateral
|
|
68080
|
+
*
|
|
68081
|
+
* This allows users to change their collateral type (e.g., JitoSOL -> mSOL) without
|
|
68082
|
+
* withdrawing and affecting their health during the swap.
|
|
68083
|
+
*
|
|
68084
|
+
* @param params - Swap collateral transaction parameters
|
|
68085
|
+
* @param params.connection - Solana connection instance
|
|
68086
|
+
* @param params.oraclePrices - Map of current oracle prices
|
|
68087
|
+
* @param params.withdrawOpts - Withdraw configuration (bank, amount, tokenProgram)
|
|
68088
|
+
* @param params.depositOpts - Deposit configuration (bank, tokenProgram)
|
|
68089
|
+
* @param params.swapOpts - Swap configuration (venue, slippage, fees)
|
|
68090
|
+
* @param params.addressLookupTableAccounts - Address lookup tables
|
|
68091
|
+
* @param params.overrideInferAccounts - Optional account overrides
|
|
68092
|
+
* @param params.additionalIxs - Additional instructions to include
|
|
68093
|
+
* @param params.crossbarUrl - Crossbar URL for oracle updates
|
|
68094
|
+
*
|
|
68095
|
+
* @returns Object containing transactions array, action index, and swap quote
|
|
68096
|
+
*
|
|
68097
|
+
* @throws {TransactionBuildingError} If swap exceeds transaction size limits
|
|
68098
|
+
* @throws {TransactionBuildingError} If Kamino reserve not found
|
|
68099
|
+
*
|
|
68100
|
+
* @see {@link makeSwapCollateralTx} for detailed implementation
|
|
68101
|
+
*/
|
|
68102
|
+
async makeSwapCollateralTx(params) {
|
|
68103
|
+
return makeSwapCollateralTx({
|
|
68104
|
+
...params,
|
|
68105
|
+
marginfiAccount: this,
|
|
68106
|
+
overrideInferAccounts: {
|
|
68107
|
+
authority: this.authority,
|
|
68108
|
+
group: this.group,
|
|
68109
|
+
...params.overrideInferAccounts
|
|
68110
|
+
}
|
|
68111
|
+
});
|
|
68112
|
+
}
|
|
68113
|
+
/**
|
|
68114
|
+
* Creates a transaction to roll a matured Exponent PT collateral position into its
|
|
68115
|
+
* next-maturity PT, in one flash-loan-wrapped bundle
|
|
68116
|
+
* (withdraw PT_old → `wrapper_merge` to base → swap-engine buy PT_new → deposit).
|
|
68117
|
+
* See {@link makeRollPtTx}.
|
|
68118
|
+
*
|
|
68119
|
+
* @param params - Roll-PT parameters (`withdrawOpts`/`depositOpts`/`swapOpts`/`rollOpts`).
|
|
68120
|
+
*/
|
|
68121
|
+
async makeRollPtTx(params) {
|
|
68122
|
+
return makeRollPtTx({
|
|
68123
|
+
...params,
|
|
68124
|
+
marginfiAccount: this,
|
|
68125
|
+
overrideInferAccounts: {
|
|
68126
|
+
authority: this.authority,
|
|
68127
|
+
group: this.group,
|
|
68128
|
+
...params.overrideInferAccounts
|
|
68129
|
+
}
|
|
68130
|
+
});
|
|
68131
|
+
}
|
|
68132
|
+
/**
|
|
68133
|
+
* Creates a transaction to swap one debt position to another using a flash loan.
|
|
68134
|
+
*
|
|
68135
|
+
* A swap debt transaction:
|
|
68136
|
+
* 1. Borrows new asset via flash loan (new debt)
|
|
68137
|
+
* 2. Swaps new asset to old debt asset (via Jupiter)
|
|
68138
|
+
* 3. Repays old debt with swapped assets
|
|
68139
|
+
*
|
|
68140
|
+
* This allows users to change their debt type (e.g., USDC debt -> SOL debt) without
|
|
68141
|
+
* repaying and affecting their health during the swap.
|
|
68142
|
+
*
|
|
68143
|
+
* @param params - Swap debt transaction parameters
|
|
68144
|
+
* @param params.connection - Solana connection instance
|
|
68145
|
+
* @param params.oraclePrices - Map of current oracle prices
|
|
68146
|
+
* @param params.repayOpts - Repay configuration (bank, amount, tokenProgram)
|
|
68147
|
+
* @param params.borrowOpts - Borrow configuration (bank, tokenProgram)
|
|
68148
|
+
* @param params.swapOpts - Swap configuration (venue, slippage, fees)
|
|
68149
|
+
* @param params.addressLookupTableAccounts - Address lookup tables
|
|
68150
|
+
* @param params.overrideInferAccounts - Optional account overrides
|
|
68151
|
+
* @param params.additionalIxs - Additional instructions to include
|
|
68152
|
+
* @param params.crossbarUrl - Crossbar URL for oracle updates
|
|
68153
|
+
*
|
|
68154
|
+
* @returns Object containing transactions array, action index, and swap quote
|
|
68155
|
+
*
|
|
68156
|
+
* @throws {TransactionBuildingError} If swap exceeds transaction size limits
|
|
68157
|
+
*
|
|
68158
|
+
* @see {@link makeSwapDebtTx} for detailed implementation
|
|
68159
|
+
*/
|
|
68160
|
+
async makeSwapDebtTx(params) {
|
|
68161
|
+
return makeSwapDebtTx({
|
|
68162
|
+
...params,
|
|
68163
|
+
marginfiAccount: this,
|
|
68164
|
+
overrideInferAccounts: {
|
|
68165
|
+
authority: this.authority,
|
|
68166
|
+
group: this.group,
|
|
68167
|
+
...params.overrideInferAccounts
|
|
68168
|
+
}
|
|
68169
|
+
});
|
|
68170
|
+
}
|
|
68171
|
+
/**
|
|
68172
|
+
* Creates a deposit transaction.
|
|
68173
|
+
*
|
|
68174
|
+
* @param params - Parameters for the deposit transaction
|
|
68175
|
+
* @returns Promise resolving to an ExtendedTransaction
|
|
68176
|
+
*
|
|
68177
|
+
* @see {@link makeDepositTx} for detailed implementation
|
|
68178
|
+
*/
|
|
68179
|
+
async makeDepositTx(params) {
|
|
68180
|
+
return makeDepositTx({
|
|
68181
|
+
...params,
|
|
68182
|
+
accountAddress: this.address,
|
|
68183
|
+
authority: this.authority,
|
|
68184
|
+
group: this.group
|
|
68185
|
+
});
|
|
68186
|
+
}
|
|
68187
|
+
/**
|
|
68188
|
+
* Creates a Drift deposit transaction.
|
|
68189
|
+
*
|
|
68190
|
+
* @param params - Parameters for the Drift deposit transaction
|
|
68191
|
+
* @returns Promise resolving to an ExtendedV0Transaction
|
|
68192
|
+
*
|
|
68193
|
+
* @see {@link makeDriftDepositTx} for detailed implementation
|
|
68194
|
+
*/
|
|
68195
|
+
async makeDriftDepositTx(params) {
|
|
68196
|
+
return makeDriftDepositTx({
|
|
68197
|
+
...params,
|
|
68198
|
+
accountAddress: this.address,
|
|
68199
|
+
authority: this.authority,
|
|
68200
|
+
group: this.group
|
|
68201
|
+
});
|
|
68202
|
+
}
|
|
68203
|
+
/**
|
|
68204
|
+
* Creates a Kamino deposit transaction.
|
|
68205
|
+
*
|
|
68206
|
+
* @param params - Parameters for the Kamino deposit transaction
|
|
68207
|
+
* @returns Promise resolving to an ExtendedV0Transaction
|
|
68208
|
+
*
|
|
68209
|
+
* @see {@link makeKaminoDepositTx} for detailed implementation
|
|
68210
|
+
*/
|
|
68211
|
+
async makeKaminoDepositTx(params) {
|
|
68212
|
+
return makeKaminoDepositTx({
|
|
68213
|
+
...params,
|
|
68214
|
+
accountAddress: this.address,
|
|
68215
|
+
authority: this.authority,
|
|
68216
|
+
group: this.group
|
|
68217
|
+
});
|
|
68218
|
+
}
|
|
68219
|
+
/**
|
|
68220
|
+
* Creates a borrow transaction.
|
|
68221
|
+
*
|
|
68222
|
+
* @param params - Parameters for the borrow transaction
|
|
68223
|
+
* @returns Promise resolving to a TransactionBuilderResult
|
|
68224
|
+
*
|
|
68225
|
+
* @see {@link makeBorrowTx} for detailed implementation
|
|
68226
|
+
*/
|
|
68227
|
+
async makeBorrowTx(params) {
|
|
68228
|
+
return makeBorrowTx({
|
|
68229
|
+
...params,
|
|
68230
|
+
marginfiAccount: this,
|
|
68231
|
+
opts: {
|
|
68232
|
+
...params.opts,
|
|
68233
|
+
overrideInferAccounts: {
|
|
68234
|
+
authority: this.authority,
|
|
68235
|
+
...params.opts?.overrideInferAccounts
|
|
68236
|
+
}
|
|
68237
|
+
}
|
|
68238
|
+
});
|
|
68239
|
+
}
|
|
68240
|
+
/**
|
|
68241
|
+
* Creates a repay transaction.
|
|
68242
|
+
*
|
|
68243
|
+
* @param params - Parameters for the repay transaction
|
|
68244
|
+
* @returns Promise resolving to an ExtendedTransaction
|
|
68245
|
+
*
|
|
68246
|
+
* @see {@link makeRepayTx} for detailed implementation
|
|
68247
|
+
*/
|
|
68248
|
+
async makeRepayTx(params) {
|
|
68249
|
+
return makeRepayTx({
|
|
68250
|
+
...params,
|
|
68251
|
+
accountAddress: this.address,
|
|
68252
|
+
authority: this.authority
|
|
68253
|
+
});
|
|
68254
|
+
}
|
|
68255
|
+
/**
|
|
68256
|
+
* Creates a withdraw transaction.
|
|
68257
|
+
*
|
|
68258
|
+
* @param params - Parameters for the withdraw transaction
|
|
68259
|
+
* @returns Promise resolving to a TransactionBuilderResult
|
|
68260
|
+
*
|
|
68261
|
+
* @see {@link makeWithdrawTx} for detailed implementation
|
|
68262
|
+
*/
|
|
68263
|
+
async makeWithdrawTx(params) {
|
|
68264
|
+
return makeWithdrawTx({
|
|
68265
|
+
...params,
|
|
68266
|
+
marginfiAccount: this,
|
|
68267
|
+
opts: {
|
|
68268
|
+
...params.opts,
|
|
68269
|
+
overrideInferAccounts: {
|
|
68270
|
+
authority: this.authority,
|
|
68271
|
+
group: this.group,
|
|
68272
|
+
...params.opts?.overrideInferAccounts
|
|
68273
|
+
}
|
|
68274
|
+
}
|
|
68275
|
+
});
|
|
68276
|
+
}
|
|
68277
|
+
/**
|
|
68278
|
+
* Creates a Drift withdraw transaction.
|
|
68279
|
+
*
|
|
68280
|
+
* @param params - Parameters for the Drift withdraw transaction
|
|
68281
|
+
* @returns Promise resolving to an ExtendedV0Transaction
|
|
68282
|
+
*
|
|
68283
|
+
* @see {@link makeDriftWithdrawTx} for detailed implementation
|
|
68284
|
+
*/
|
|
68285
|
+
async makeDriftWithdrawTx(params) {
|
|
68286
|
+
return makeDriftWithdrawTx({
|
|
68287
|
+
...params,
|
|
68288
|
+
marginfiAccount: this,
|
|
68289
|
+
opts: {
|
|
68290
|
+
...params.opts,
|
|
68291
|
+
overrideInferAccounts: {
|
|
68292
|
+
authority: this.authority,
|
|
68293
|
+
group: this.group,
|
|
68294
|
+
...params.opts?.overrideInferAccounts
|
|
68295
|
+
}
|
|
68296
|
+
}
|
|
68297
|
+
});
|
|
68298
|
+
}
|
|
68299
|
+
/**
|
|
68300
|
+
* Creates a Kamino withdraw transaction.
|
|
68301
|
+
*
|
|
68302
|
+
* @param params - Parameters for the Kamino withdraw transaction
|
|
68303
|
+
* @returns Promise resolving to a TransactionBuilderResult
|
|
68304
|
+
*
|
|
68305
|
+
* @see {@link makeKaminoWithdrawTx} for detailed implementation
|
|
68306
|
+
*/
|
|
68307
|
+
async makeKaminoWithdrawTx(params) {
|
|
68308
|
+
return makeKaminoWithdrawTx({
|
|
68309
|
+
...params,
|
|
68310
|
+
marginfiAccount: this,
|
|
68311
|
+
opts: {
|
|
68312
|
+
...params.opts,
|
|
68313
|
+
overrideInferAccounts: {
|
|
68314
|
+
authority: this.authority,
|
|
68315
|
+
group: this.group,
|
|
68316
|
+
...params.opts?.overrideInferAccounts
|
|
68317
|
+
}
|
|
68318
|
+
}
|
|
68319
|
+
});
|
|
68320
|
+
}
|
|
68321
|
+
/**
|
|
68322
|
+
* Creates a flash loan transaction.
|
|
68323
|
+
*
|
|
68324
|
+
* @param params - Parameters for the flash loan transaction
|
|
68325
|
+
* @returns Promise resolving to flash loan transaction details
|
|
68326
|
+
*
|
|
68327
|
+
* @see {@link makeFlashLoanTx} for detailed implementation
|
|
68328
|
+
*/
|
|
68329
|
+
async makeFlashLoanTx(params) {
|
|
68330
|
+
return makeFlashLoanTx({
|
|
68331
|
+
...params,
|
|
68332
|
+
marginfiAccount: this
|
|
68333
|
+
});
|
|
68334
|
+
}
|
|
68335
|
+
};
|
|
68336
|
+
|
|
68337
|
+
// src/services/account/actions/bridge-swap.ts
|
|
68338
|
+
var MAX_BRIDGED_BUNDLE_TXS = 5;
|
|
68339
|
+
function classifyTxs(txs) {
|
|
68340
|
+
const out = { setups: [], cranks: [], flashloans: [] };
|
|
68341
|
+
for (const tx of txs) {
|
|
68342
|
+
if (tx.type === "CREATE_ATA" /* CREATE_ATA */) out.setups.push(tx);
|
|
68343
|
+
else if (tx.type === "CRANK" /* CRANK */) out.cranks.push(tx);
|
|
68344
|
+
else out.flashloans.push(tx);
|
|
68345
|
+
}
|
|
68346
|
+
return out;
|
|
68347
|
+
}
|
|
68348
|
+
function ixIdentity(ix) {
|
|
68349
|
+
const keys = ix.keys.map((k) => k.pubkey.toBase58()).join(",");
|
|
68350
|
+
return `${ix.programId.toBase58()}|${keys}|${Buffer.from(ix.data).toString("base64")}`;
|
|
68351
|
+
}
|
|
68352
|
+
function mergeSetupTxs(txs, payer, blockhash) {
|
|
68353
|
+
if (txs.length === 0) return null;
|
|
68354
|
+
if (txs.length === 1) return txs[0];
|
|
68355
|
+
const lutMap = /* @__PURE__ */ new Map();
|
|
68356
|
+
const seen = /* @__PURE__ */ new Set();
|
|
68357
|
+
const ixs = [];
|
|
68358
|
+
for (const tx of txs) {
|
|
68359
|
+
const luts2 = tx.addressLookupTables ?? [];
|
|
68360
|
+
luts2.forEach((l) => lutMap.set(l.key.toBase58(), l));
|
|
68361
|
+
const msg = decompileV0Transaction(tx, luts2);
|
|
68362
|
+
for (const ix of msg.instructions) {
|
|
68363
|
+
const id = ixIdentity(ix);
|
|
68364
|
+
if (seen.has(id)) continue;
|
|
68365
|
+
seen.add(id);
|
|
68366
|
+
ixs.push(ix);
|
|
68367
|
+
}
|
|
68368
|
+
}
|
|
68369
|
+
const luts = [...lutMap.values()];
|
|
68370
|
+
const split = splitInstructionsToFitTransactions([], ixs, { blockhash, payerKey: payer, luts });
|
|
68371
|
+
if (split.length !== 1) return null;
|
|
68372
|
+
return addTransactionMetadata(split[0], {
|
|
68373
|
+
type: "CREATE_ATA" /* CREATE_ATA */,
|
|
68374
|
+
addressLookupTables: luts
|
|
68375
|
+
});
|
|
68376
|
+
}
|
|
68377
|
+
function projectAccountAfterFirstLeg(account, firstLegFlashloanTxs, program, banksMap, multipliers) {
|
|
68378
|
+
const ixs = [];
|
|
68379
|
+
for (const tx of firstLegFlashloanTxs) {
|
|
68380
|
+
const luts = tx.addressLookupTables ?? [];
|
|
68381
|
+
ixs.push(...decompileV0Transaction(tx, luts).instructions);
|
|
68382
|
+
}
|
|
68383
|
+
const { projectedBalances } = computeProjectedActiveBalancesNoCpi({
|
|
68384
|
+
account,
|
|
68385
|
+
instructions: ixs,
|
|
68386
|
+
program,
|
|
68387
|
+
banksMap,
|
|
68388
|
+
assetShareValueMultiplierByBank: multipliers
|
|
68389
|
+
});
|
|
68390
|
+
return new MarginfiAccount(
|
|
68391
|
+
account.address,
|
|
68392
|
+
account.group,
|
|
68393
|
+
account.authority,
|
|
68394
|
+
projectedBalances.map((b) => Balance.fromBalanceType(b)),
|
|
68395
|
+
account.accountFlags,
|
|
68396
|
+
account.emissionsDestinationAccount,
|
|
68397
|
+
account.healthCache
|
|
68398
|
+
);
|
|
68399
|
+
}
|
|
68400
|
+
function composeBundle(firstLegTxs, secondLegTxs, payer, blockhash, maxBundleTxs) {
|
|
68401
|
+
const c1 = classifyTxs(firstLegTxs);
|
|
68402
|
+
const c2 = classifyTxs(secondLegTxs);
|
|
68403
|
+
const mergedSetup = mergeSetupTxs([...c1.setups, ...c2.setups], payer, blockhash);
|
|
68404
|
+
if ([...c1.setups, ...c2.setups].length > 0 && !mergedSetup) return null;
|
|
68405
|
+
const result = [
|
|
68406
|
+
...mergedSetup ? [mergedSetup] : [],
|
|
68407
|
+
...c1.cranks,
|
|
68408
|
+
...c1.flashloans,
|
|
68409
|
+
// firstLegFL(s)
|
|
68410
|
+
...c2.cranks,
|
|
68411
|
+
...c2.flashloans
|
|
68412
|
+
// secondLegFL(s)
|
|
68413
|
+
];
|
|
68414
|
+
if (result.length > maxBundleTxs) return null;
|
|
68415
|
+
return result;
|
|
68416
|
+
}
|
|
68417
|
+
function compoundQuoteRisk(firstLeg, secondLeg) {
|
|
68418
|
+
const compound = (a, b) => {
|
|
68419
|
+
if (a == null && b == null) return void 0;
|
|
68420
|
+
const x = Number(a ?? 0);
|
|
68421
|
+
const y = Number(b ?? 0);
|
|
68422
|
+
return String(1 - (1 - x) * (1 - y));
|
|
68423
|
+
};
|
|
68424
|
+
return {
|
|
68425
|
+
slippageBps: Math.round(
|
|
68426
|
+
(1 - (1 - firstLeg.slippageBps / 1e4) * (1 - secondLeg.slippageBps / 1e4)) * 1e4
|
|
68427
|
+
),
|
|
68428
|
+
priceImpactPct: compound(firstLeg.priceImpactPct, secondLeg.priceImpactPct)
|
|
68429
|
+
};
|
|
68430
|
+
}
|
|
68431
|
+
function mergeBridgeQuotes(firstLeg, secondLeg) {
|
|
68432
|
+
return {
|
|
68433
|
+
inAmount: firstLeg.inAmount,
|
|
68434
|
+
outAmount: secondLeg.outAmount,
|
|
68435
|
+
otherAmountThreshold: secondLeg.otherAmountThreshold,
|
|
68436
|
+
...compoundQuoteRisk(firstLeg, secondLeg),
|
|
68437
|
+
provider: firstLeg.provider
|
|
68438
|
+
};
|
|
68439
|
+
}
|
|
68440
|
+
function mergeBridgeQuotesDebt(firstLeg, secondLeg) {
|
|
68441
|
+
return {
|
|
68442
|
+
inAmount: firstLeg.outAmount,
|
|
68443
|
+
outAmount: secondLeg.inAmount,
|
|
68444
|
+
otherAmountThreshold: secondLeg.inAmount,
|
|
68445
|
+
...compoundQuoteRisk(firstLeg, secondLeg),
|
|
68446
|
+
provider: firstLeg.provider
|
|
68447
|
+
};
|
|
68448
|
+
}
|
|
68449
|
+
function mergeBridgeQuotesLoop(firstLeg, secondLeg) {
|
|
68450
|
+
return {
|
|
68451
|
+
inAmount: secondLeg.inAmount,
|
|
68452
|
+
outAmount: firstLeg.outAmount,
|
|
68453
|
+
otherAmountThreshold: firstLeg.otherAmountThreshold,
|
|
68454
|
+
...compoundQuoteRisk(firstLeg, secondLeg),
|
|
68455
|
+
provider: firstLeg.provider
|
|
68456
|
+
};
|
|
68457
|
+
}
|
|
68458
|
+
function blockhashOf(leg) {
|
|
68459
|
+
const tx = leg.transactions[0];
|
|
68460
|
+
return tx ? tx.message.recentBlockhash : web3_js.PublicKey.default.toBase58();
|
|
68461
|
+
}
|
|
68462
|
+
async function composeBridgedSwap(params) {
|
|
68463
|
+
const {
|
|
68464
|
+
firstLeg,
|
|
68465
|
+
buildSecondLeg,
|
|
68466
|
+
marginfiAccount,
|
|
68467
|
+
program,
|
|
68468
|
+
banksMap,
|
|
68469
|
+
assetShareValueMultiplierByBank,
|
|
68470
|
+
feePayer,
|
|
68471
|
+
maxBundleTxs = MAX_BRIDGED_BUNDLE_TXS
|
|
68472
|
+
} = params;
|
|
68473
|
+
if (!firstLeg.quoteResponse) return null;
|
|
68474
|
+
const projectedAccount = projectAccountAfterFirstLeg(
|
|
68475
|
+
marginfiAccount,
|
|
68476
|
+
classifyTxs(firstLeg.transactions).flashloans,
|
|
68477
|
+
program,
|
|
68478
|
+
banksMap,
|
|
68479
|
+
assetShareValueMultiplierByBank
|
|
68480
|
+
);
|
|
68481
|
+
const secondLeg = await buildSecondLeg(projectedAccount);
|
|
68482
|
+
if (!secondLeg.quoteResponse) return null;
|
|
68483
|
+
const transactions = composeBundle(
|
|
68484
|
+
firstLeg.transactions,
|
|
68485
|
+
secondLeg.transactions,
|
|
68486
|
+
feePayer,
|
|
68487
|
+
blockhashOf(firstLeg),
|
|
68488
|
+
maxBundleTxs
|
|
68489
|
+
);
|
|
68490
|
+
if (!transactions) return null;
|
|
68491
|
+
return { transactions, firstLegQuote: firstLeg.quoteResponse, secondLegQuote: secondLeg.quoteResponse };
|
|
68492
|
+
}
|
|
68493
|
+
async function makeSwapDebtTx(params) {
|
|
68494
|
+
const {
|
|
68495
|
+
program,
|
|
68496
|
+
marginfiAccount,
|
|
68497
|
+
connection,
|
|
68498
|
+
bankMap,
|
|
68499
|
+
oraclePrices,
|
|
68500
|
+
repayOpts,
|
|
68501
|
+
borrowOpts,
|
|
68502
|
+
bankMetadataMap,
|
|
68503
|
+
assetShareValueMultiplierByBank,
|
|
68504
|
+
addressLookupTableAccounts,
|
|
68505
|
+
crossbarUrl,
|
|
68506
|
+
additionalIxs = []
|
|
68507
|
+
} = params;
|
|
68508
|
+
const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
|
|
68509
|
+
const setupIxs = await makeSetupIx({
|
|
68510
|
+
connection,
|
|
68511
|
+
authority: marginfiAccount.authority,
|
|
68512
|
+
tokens: [
|
|
68513
|
+
{ mint: repayOpts.repayBank.mint, tokenProgram: repayOpts.tokenProgram },
|
|
68514
|
+
{ mint: borrowOpts.borrowBank.mint, tokenProgram: borrowOpts.tokenProgram }
|
|
68515
|
+
]
|
|
68516
|
+
});
|
|
68517
|
+
const refreshIntegrationIxs = makeRefreshIntegrationBanksIxs(
|
|
68518
|
+
marginfiAccount,
|
|
68519
|
+
bankMap,
|
|
68520
|
+
[],
|
|
68521
|
+
bankMetadataMap,
|
|
68522
|
+
[repayOpts.repayBank.address, borrowOpts.borrowBank.address]
|
|
68523
|
+
);
|
|
68524
|
+
const { flashloanTx, setupInstructions, swapQuote, borrowIxs, repayIxs } = await buildSwapDebtFlashloanTx({
|
|
68525
|
+
...params,
|
|
68526
|
+
blockhash
|
|
68527
|
+
});
|
|
68528
|
+
const jupiterSetupInstructions = setupInstructions.filter((ix) => {
|
|
68529
|
+
if (ix.programId.equals(web3_js.ComputeBudgetProgram.programId)) {
|
|
68530
|
+
return false;
|
|
68531
|
+
}
|
|
68532
|
+
if (ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
|
|
68533
|
+
const mintKey = ix.keys[3]?.pubkey;
|
|
68534
|
+
if (mintKey?.equals(repayOpts.repayBank.mint) || mintKey?.equals(borrowOpts.borrowBank.mint)) {
|
|
68535
|
+
return false;
|
|
68536
|
+
}
|
|
68537
|
+
}
|
|
68538
|
+
return true;
|
|
68539
|
+
});
|
|
68540
|
+
setupIxs.push(...jupiterSetupInstructions);
|
|
68541
|
+
const { instructions: updateFeedIxs, luts: feedLuts } = await makeSmartCrankSwbFeedIx({
|
|
68542
|
+
marginfiAccount,
|
|
68543
|
+
bankMap,
|
|
68544
|
+
oraclePrices,
|
|
68545
|
+
assetShareValueMultiplierByBank,
|
|
68546
|
+
instructions: [...borrowIxs.instructions, ...repayIxs.instructions],
|
|
68547
|
+
program,
|
|
68548
|
+
connection,
|
|
68549
|
+
crossbarUrl
|
|
68550
|
+
});
|
|
68551
|
+
let additionalTxs = [];
|
|
68552
|
+
if (setupIxs.length > 0 || refreshIntegrationIxs.instructions.length > 0) {
|
|
68553
|
+
const ixs = [...additionalIxs, ...setupIxs, ...refreshIntegrationIxs.instructions];
|
|
68554
|
+
const txs = splitInstructionsToFitTransactions([], ixs, {
|
|
68555
|
+
blockhash,
|
|
68556
|
+
payerKey: marginfiAccount.authority,
|
|
68557
|
+
luts: addressLookupTableAccounts ?? []
|
|
68558
|
+
});
|
|
68559
|
+
additionalTxs.push(
|
|
68560
|
+
...txs.map(
|
|
68561
|
+
(tx) => addTransactionMetadata(tx, {
|
|
68562
|
+
type: "CREATE_ATA" /* CREATE_ATA */,
|
|
68563
|
+
addressLookupTables: addressLookupTableAccounts
|
|
68564
|
+
})
|
|
68565
|
+
)
|
|
68566
|
+
);
|
|
68567
|
+
}
|
|
68568
|
+
if (updateFeedIxs.length > 0) {
|
|
68569
|
+
const message = new web3_js.TransactionMessage({
|
|
68570
|
+
payerKey: marginfiAccount.authority,
|
|
68571
|
+
recentBlockhash: blockhash,
|
|
68572
|
+
instructions: updateFeedIxs
|
|
68573
|
+
}).compileToV0Message(feedLuts);
|
|
68574
|
+
additionalTxs.push(
|
|
68575
|
+
addTransactionMetadata(new web3_js.VersionedTransaction(message), {
|
|
68576
|
+
addressLookupTables: feedLuts,
|
|
68577
|
+
type: "CRANK" /* CRANK */
|
|
68578
|
+
})
|
|
68579
|
+
);
|
|
68580
|
+
}
|
|
68581
|
+
const transactions = [...additionalTxs, flashloanTx];
|
|
68582
|
+
return {
|
|
68583
|
+
transactions,
|
|
68584
|
+
actionTxIndex: transactions.length - 1,
|
|
68585
|
+
quoteResponse: swapQuote
|
|
68586
|
+
};
|
|
68587
|
+
}
|
|
68588
|
+
async function buildSwapDebtFlashloanTx({
|
|
68589
|
+
program,
|
|
68590
|
+
marginfiAccount,
|
|
68591
|
+
bankMap,
|
|
68592
|
+
repayOpts,
|
|
68593
|
+
borrowOpts,
|
|
68594
|
+
swapOpts,
|
|
68595
|
+
bankMetadataMap,
|
|
68596
|
+
addressLookupTableAccounts,
|
|
68597
|
+
connection,
|
|
68598
|
+
overrideInferAccounts,
|
|
68599
|
+
blockhash,
|
|
68600
|
+
swapEngineRunner
|
|
68601
|
+
}) {
|
|
68602
|
+
const {
|
|
68603
|
+
repayBank,
|
|
68604
|
+
tokenProgram: repayTokenProgram,
|
|
68605
|
+
totalPositionAmount,
|
|
68606
|
+
repayAmount
|
|
68607
|
+
} = repayOpts;
|
|
68608
|
+
const { borrowBank, tokenProgram: borrowTokenProgram } = borrowOpts;
|
|
68609
|
+
if (repayAmount !== void 0 && repayAmount <= 0) {
|
|
68610
|
+
throw new Error("repayAmount must be greater than 0");
|
|
68611
|
+
}
|
|
68612
|
+
const actualRepayAmount = Math.min(repayAmount ?? totalPositionAmount, totalPositionAmount);
|
|
68613
|
+
const cuRequestIxs = [
|
|
68614
|
+
web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
|
|
68615
|
+
web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
|
|
68616
|
+
];
|
|
68617
|
+
const destinationTokenAccount = getAssociatedTokenAddressSync(
|
|
68618
|
+
repayBank.mint,
|
|
68619
|
+
marginfiAccount.authority,
|
|
68620
|
+
true,
|
|
68621
|
+
repayTokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
|
|
68622
|
+
);
|
|
68623
|
+
const estimatedBorrowAmount = computeBorrowEstimateForRepay({
|
|
68624
|
+
repayTargetUi: actualRepayAmount,
|
|
68625
|
+
repayMarketPrice: repayOpts.marketPrice,
|
|
68626
|
+
borrowMarketPrice: borrowOpts.marketPrice,
|
|
68627
|
+
slippageBps: swapOpts.swapConfig?.slippageBps,
|
|
68628
|
+
isRepayAll: actualRepayAmount >= totalPositionAmount
|
|
68629
|
+
});
|
|
68630
|
+
const swapConstraints = await computeFlashloanSwapConstraints({
|
|
68631
|
+
program,
|
|
68632
|
+
marginfiAccount,
|
|
68633
|
+
bankMap,
|
|
68634
|
+
bankMetadataMap,
|
|
68635
|
+
addressLookupTableAccounts: addressLookupTableAccounts ?? [],
|
|
68636
|
+
primaryIx: { type: "borrow", bank: borrowBank, tokenProgram: borrowTokenProgram },
|
|
68637
|
+
secondaryIx: { type: "repay", bank: repayBank, tokenProgram: repayTokenProgram },
|
|
68638
|
+
overrideInferAccounts
|
|
68639
|
+
});
|
|
68640
|
+
const footprintBorrowIxs = await makeBorrowIx3({
|
|
68641
|
+
program,
|
|
68642
|
+
bank: borrowBank,
|
|
68643
|
+
bankMap,
|
|
68644
|
+
tokenProgram: borrowTokenProgram,
|
|
68645
|
+
amount: estimatedBorrowAmount,
|
|
68646
|
+
marginfiAccount,
|
|
68647
|
+
authority: marginfiAccount.authority,
|
|
68648
|
+
isSync: true,
|
|
68649
|
+
opts: { createAtas: false, wrapAndUnwrapSol: false, overrideInferAccounts }
|
|
68650
|
+
});
|
|
68651
|
+
const footprintRepayIxs = await makeRepayIx3({
|
|
68652
|
+
program,
|
|
68653
|
+
bank: repayBank,
|
|
68654
|
+
tokenProgram: repayTokenProgram,
|
|
68655
|
+
amount: actualRepayAmount,
|
|
68656
|
+
accountAddress: marginfiAccount.address,
|
|
68657
|
+
authority: marginfiAccount.authority,
|
|
68658
|
+
isSync: true,
|
|
68659
|
+
opts: { wrapAndUnwrapSol: false, overrideInferAccounts }
|
|
68660
|
+
});
|
|
68661
|
+
const runEngine = swapEngineRunner ?? runSwapEngine;
|
|
68662
|
+
const engineResult = await runEngine({
|
|
68663
|
+
inputMint: borrowBank.mint.toBase58(),
|
|
68664
|
+
outputMint: repayBank.mint.toBase58(),
|
|
68665
|
+
amountNative: uiToNative(estimatedBorrowAmount, borrowBank.mintDecimals).toNumber(),
|
|
68666
|
+
inputDecimals: borrowBank.mintDecimals,
|
|
68667
|
+
outputDecimals: repayBank.mintDecimals,
|
|
68668
|
+
...swapEngineQuoteFieldsFromOpts(swapOpts),
|
|
68669
|
+
taker: marginfiAccount.authority,
|
|
68670
|
+
destinationTokenAccount,
|
|
68671
|
+
connection,
|
|
68672
|
+
footprint: {
|
|
68673
|
+
instructions: [
|
|
68674
|
+
...cuRequestIxs,
|
|
68675
|
+
...footprintBorrowIxs.instructions,
|
|
68676
|
+
...footprintRepayIxs.instructions
|
|
68677
|
+
],
|
|
68678
|
+
luts: addressLookupTableAccounts ?? [],
|
|
68679
|
+
payer: marginfiAccount.authority,
|
|
68680
|
+
sizeConstraint: swapConstraints.sizeConstraint,
|
|
68681
|
+
maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
|
|
68682
|
+
},
|
|
68683
|
+
providers: swapEngineProvidersFromOpts(swapOpts)
|
|
68684
|
+
});
|
|
68685
|
+
const quoteResponse = engineResult.quoteResponse;
|
|
68686
|
+
const outAmount = nativeToUi(quoteResponse.outAmount, repayBank.mintDecimals);
|
|
68687
|
+
const outAmountThreshold = nativeToUi(quoteResponse.otherAmountThreshold, repayBank.mintDecimals);
|
|
68688
|
+
const amountToRepay = outAmount > totalPositionAmount ? totalPositionAmount : outAmountThreshold;
|
|
68689
|
+
const borrowAmount = nativeToUi(quoteResponse.inAmount, borrowBank.mintDecimals);
|
|
68690
|
+
const borrowIxs = await makeBorrowIx3({
|
|
68691
|
+
program,
|
|
68692
|
+
bank: borrowBank,
|
|
68693
|
+
bankMap,
|
|
68694
|
+
tokenProgram: borrowTokenProgram,
|
|
68695
|
+
amount: borrowAmount,
|
|
68696
|
+
marginfiAccount,
|
|
68697
|
+
authority: marginfiAccount.authority,
|
|
68698
|
+
isSync: true,
|
|
68699
|
+
opts: {
|
|
68700
|
+
createAtas: false,
|
|
68701
|
+
wrapAndUnwrapSol: false,
|
|
68702
|
+
overrideInferAccounts
|
|
68703
|
+
}
|
|
68704
|
+
});
|
|
68705
|
+
const repayIxs = await makeRepayIx3({
|
|
68706
|
+
program,
|
|
68707
|
+
bank: repayBank,
|
|
68708
|
+
tokenProgram: repayTokenProgram,
|
|
68709
|
+
amount: amountToRepay,
|
|
68710
|
+
accountAddress: marginfiAccount.address,
|
|
68711
|
+
authority: marginfiAccount.authority,
|
|
68712
|
+
repayAll: isWholePosition(
|
|
68713
|
+
{
|
|
68714
|
+
amount: totalPositionAmount,
|
|
68715
|
+
isLending: false
|
|
68716
|
+
},
|
|
68717
|
+
amountToRepay,
|
|
68718
|
+
repayBank.mintDecimals
|
|
68719
|
+
),
|
|
68720
|
+
isSync: true,
|
|
68721
|
+
opts: {
|
|
68722
|
+
wrapAndUnwrapSol: false,
|
|
68723
|
+
overrideInferAccounts
|
|
68724
|
+
}
|
|
68725
|
+
});
|
|
68726
|
+
const luts = [...addressLookupTableAccounts ?? [], ...engineResult.swapLuts];
|
|
68727
|
+
const allNonFlIxs = [
|
|
68728
|
+
...cuRequestIxs,
|
|
68729
|
+
...borrowIxs.instructions,
|
|
68730
|
+
...engineResult.swapInstructions,
|
|
68731
|
+
...repayIxs.instructions
|
|
68732
|
+
];
|
|
68733
|
+
compileFlashloanPrecheck({
|
|
68734
|
+
allIxs: allNonFlIxs,
|
|
68735
|
+
payer: marginfiAccount.authority,
|
|
68736
|
+
luts,
|
|
68737
|
+
sizeConstraint: swapConstraints.sizeConstraint,
|
|
68738
|
+
swapIxCount: engineResult.swapInstructions.length,
|
|
68739
|
+
swapLutCount: engineResult.swapLuts.length
|
|
68740
|
+
});
|
|
68741
|
+
const flashloanTx = await makeFlashLoanTx({
|
|
68742
|
+
program,
|
|
68743
|
+
marginfiAccount,
|
|
68744
|
+
bankMap,
|
|
68745
|
+
addressLookupTableAccounts: luts,
|
|
68746
|
+
blockhash,
|
|
68747
|
+
ixs: allNonFlIxs,
|
|
68748
|
+
isSync: true
|
|
68749
|
+
});
|
|
68750
|
+
const txSize = getTxSize(flashloanTx);
|
|
68751
|
+
const totalKeys = getTotalAccountKeys(flashloanTx);
|
|
68752
|
+
if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
|
|
68753
|
+
throw TransactionBuildingError.swapSizeExceededPositionSwap(
|
|
68754
|
+
txSize,
|
|
68755
|
+
totalKeys,
|
|
68756
|
+
swapOpts.swapConfig?.provider
|
|
68757
|
+
);
|
|
68758
|
+
}
|
|
68759
|
+
return {
|
|
68760
|
+
flashloanTx,
|
|
68761
|
+
setupInstructions: engineResult.setupInstructions,
|
|
68762
|
+
swapQuote: quoteResponse,
|
|
68763
|
+
borrowIxs,
|
|
68764
|
+
repayIxs
|
|
68765
|
+
};
|
|
68766
|
+
}
|
|
68767
|
+
async function makeBridgedSwapDebtTx(params) {
|
|
68768
|
+
const { bridgeOpts, ...directParams } = params;
|
|
68769
|
+
try {
|
|
68770
|
+
return await makeSwapDebtTx(directParams);
|
|
68771
|
+
} catch (directError) {
|
|
68772
|
+
if (!isDecomposableSwapError(directError)) throw directError;
|
|
68773
|
+
const bridged = await tryBridgedDebtSwap(directParams, bridgeOpts);
|
|
68774
|
+
if (bridged) return bridged;
|
|
68775
|
+
throw directError;
|
|
68776
|
+
}
|
|
68777
|
+
}
|
|
68778
|
+
async function tryBridgedDebtSwap(params, bridgeOpts) {
|
|
68779
|
+
const sourceBank = params.repayOpts.repayBank;
|
|
68780
|
+
const destinationBank = params.borrowOpts.borrowBank;
|
|
68781
|
+
const repayAmount = params.repayOpts.repayAmount ?? params.repayOpts.totalPositionAmount;
|
|
68782
|
+
const oraclePriceOf = (bank) => params.oraclePrices.get(bank.address.toBase58())?.priceRealtime.price.toNumber() ?? 0;
|
|
68783
|
+
const { usableBridgeBanks, conflictingBridgeBanks } = selectSwapBridges({
|
|
68784
|
+
sourceMint: sourceBank.mint,
|
|
68785
|
+
destinationMint: destinationBank.mint,
|
|
68786
|
+
bankMap: params.bankMap,
|
|
68787
|
+
marginfiAccount: params.marginfiAccount,
|
|
68788
|
+
bridgeTokenSide: "borrow",
|
|
68789
|
+
bridgeCandidateMints: bridgeOpts?.bridgeCandidateMints
|
|
68790
|
+
});
|
|
68791
|
+
const tokenProgramCache = new Map(bridgeOpts?.tokenProgramByMint);
|
|
68792
|
+
return tryBridgeCandidates({
|
|
68793
|
+
usableBridgeBanks,
|
|
68794
|
+
conflictingBridgeBanks,
|
|
68795
|
+
bridgeTokenSide: "borrow",
|
|
68796
|
+
abortSignal: bridgeOpts?.abortSignal,
|
|
68797
|
+
buildBundleThroughBridge: async (bridgeBank) => {
|
|
68798
|
+
const bridgeTokenProgram = await resolveTokenProgramForMint(
|
|
68799
|
+
bridgeBank.mint,
|
|
68800
|
+
params.connection,
|
|
68801
|
+
tokenProgramCache
|
|
68802
|
+
);
|
|
68803
|
+
const firstLeg = await makeSwapDebtTx({
|
|
68804
|
+
...sharedBridgeLegContext(params),
|
|
68805
|
+
repayOpts: {
|
|
68806
|
+
totalPositionAmount: params.repayOpts.totalPositionAmount,
|
|
68807
|
+
repayAmount,
|
|
68808
|
+
repayBank: sourceBank,
|
|
68809
|
+
tokenProgram: params.repayOpts.tokenProgram,
|
|
68810
|
+
marketPrice: oraclePriceOf(sourceBank)
|
|
68811
|
+
},
|
|
68812
|
+
borrowOpts: {
|
|
68813
|
+
borrowBank: bridgeBank,
|
|
68814
|
+
tokenProgram: bridgeTokenProgram,
|
|
68815
|
+
marketPrice: oraclePriceOf(bridgeBank)
|
|
68816
|
+
}
|
|
68817
|
+
});
|
|
68818
|
+
if (!firstLeg.quoteResponse) return null;
|
|
68819
|
+
const bridgeBorrowedUi = nativeToUi(firstLeg.quoteResponse.inAmount, bridgeBank.mintDecimals);
|
|
68820
|
+
if (bridgeBorrowedUi <= 0) return null;
|
|
68821
|
+
const result = await composeBridgedSwap({
|
|
68822
|
+
firstLeg,
|
|
68823
|
+
buildSecondLeg: (projectedAccount) => makeSwapDebtTx({
|
|
68824
|
+
...sharedBridgeLegContext(params),
|
|
68825
|
+
marginfiAccount: projectedAccount,
|
|
68826
|
+
repayOpts: {
|
|
68827
|
+
totalPositionAmount: bridgeBorrowedUi,
|
|
68828
|
+
repayAmount: bridgeBorrowedUi,
|
|
68829
|
+
repayBank: bridgeBank,
|
|
68830
|
+
tokenProgram: bridgeTokenProgram,
|
|
68831
|
+
marketPrice: oraclePriceOf(bridgeBank)
|
|
68832
|
+
},
|
|
68833
|
+
borrowOpts: {
|
|
68834
|
+
borrowBank: destinationBank,
|
|
68835
|
+
tokenProgram: params.borrowOpts.tokenProgram,
|
|
68836
|
+
marketPrice: oraclePriceOf(destinationBank)
|
|
68837
|
+
}
|
|
68838
|
+
}),
|
|
68839
|
+
marginfiAccount: params.marginfiAccount,
|
|
68840
|
+
program: params.program,
|
|
68841
|
+
banksMap: params.bankMap,
|
|
68842
|
+
assetShareValueMultiplierByBank: params.assetShareValueMultiplierByBank,
|
|
68843
|
+
feePayer: params.overrideInferAccounts?.authority ?? params.marginfiAccount.authority,
|
|
68844
|
+
maxBundleTxs: bridgeOpts?.maxBundleTxs
|
|
68845
|
+
});
|
|
68846
|
+
if (!result) return null;
|
|
68847
|
+
return {
|
|
68848
|
+
transactions: result.transactions,
|
|
68849
|
+
actionTxIndex: result.transactions.length - 1,
|
|
68850
|
+
quoteResponse: mergeBridgeQuotesDebt(result.firstLegQuote, result.secondLegQuote),
|
|
68851
|
+
bridgeMint: bridgeBank.mint
|
|
68852
|
+
};
|
|
68853
|
+
}
|
|
68854
|
+
});
|
|
68855
|
+
}
|
|
68856
|
+
|
|
68857
|
+
// src/services/account/actions/loop.ts
|
|
68858
|
+
async function makeLoopTx(params) {
|
|
68859
|
+
const {
|
|
68860
|
+
program,
|
|
68861
|
+
marginfiAccount,
|
|
68862
|
+
bankMap,
|
|
68863
|
+
depositOpts,
|
|
68864
|
+
borrowOpts,
|
|
68865
|
+
bankMetadataMap,
|
|
68866
|
+
addressLookupTableAccounts,
|
|
68867
|
+
connection,
|
|
68868
|
+
oraclePrices,
|
|
68869
|
+
crossbarUrl,
|
|
68870
|
+
additionalIxs = []
|
|
68871
|
+
} = params;
|
|
68872
|
+
const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
|
|
68873
|
+
const setupIxs = await makeSetupIx({
|
|
68874
|
+
connection,
|
|
68875
|
+
authority: marginfiAccount.authority,
|
|
68876
|
+
tokens: [
|
|
68877
|
+
{
|
|
68878
|
+
mint: borrowOpts.borrowBank.mint,
|
|
68879
|
+
tokenProgram: borrowOpts.tokenProgram
|
|
68880
|
+
},
|
|
68881
|
+
{
|
|
68882
|
+
mint: depositOpts.depositBank.mint,
|
|
68883
|
+
tokenProgram: depositOpts.tokenProgram
|
|
68884
|
+
}
|
|
68885
|
+
]
|
|
68886
|
+
});
|
|
68887
|
+
const refreshIntegrationIxs = makeRefreshIntegrationBanksIxs(
|
|
68888
|
+
params.marginfiAccount,
|
|
68889
|
+
params.bankMap,
|
|
68890
|
+
[depositOpts.depositBank.address],
|
|
68891
|
+
params.bankMetadataMap,
|
|
68892
|
+
[borrowOpts.borrowBank.address, depositOpts.depositBank.address]
|
|
68893
|
+
);
|
|
68894
|
+
const { flashloanTx, setupInstructions, swapQuote, depositIxs, borrowIxs } = await buildLoopFlashloanTx({
|
|
68895
|
+
...params,
|
|
68896
|
+
blockhash
|
|
68897
|
+
});
|
|
68898
|
+
const jupiterSetupInstructions = setupInstructions.filter((ix) => {
|
|
68899
|
+
if (ix.programId.equals(web3_js.ComputeBudgetProgram.programId)) {
|
|
68900
|
+
return false;
|
|
68901
|
+
}
|
|
68902
|
+
if (ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
|
|
68903
|
+
const mintKey = ix.keys[3]?.pubkey;
|
|
68904
|
+
if (mintKey?.equals(depositOpts.depositBank.mint) || mintKey?.equals(borrowOpts.borrowBank.mint)) {
|
|
68905
|
+
return false;
|
|
68906
|
+
}
|
|
68907
|
+
}
|
|
68908
|
+
return true;
|
|
68909
|
+
});
|
|
68910
|
+
setupIxs.push(...jupiterSetupInstructions);
|
|
68911
|
+
const { instructions: updateFeedIxs, luts: feedLuts } = await makeSmartCrankSwbFeedIx({
|
|
68912
|
+
marginfiAccount,
|
|
68913
|
+
bankMap,
|
|
68914
|
+
oraclePrices,
|
|
68915
|
+
assetShareValueMultiplierByBank: params.assetShareValueMultiplierByBank,
|
|
68916
|
+
instructions: [...borrowIxs.instructions, ...depositIxs.instructions],
|
|
68917
|
+
program,
|
|
68918
|
+
connection,
|
|
68919
|
+
crossbarUrl
|
|
68920
|
+
});
|
|
68921
|
+
let additionalTxs = [];
|
|
68922
|
+
if (depositOpts.depositBank.mint.equals(NATIVE_MINT) && depositOpts.inputDepositAmount) {
|
|
68923
|
+
setupIxs.push(
|
|
68924
|
+
...makeWrapSolIxs(marginfiAccount.authority, new BigNumber3.BigNumber(depositOpts.inputDepositAmount))
|
|
68925
|
+
);
|
|
68926
|
+
}
|
|
68927
|
+
if (setupIxs.length > 0 || additionalIxs.length > 0 || refreshIntegrationIxs.instructions.length > 0) {
|
|
68928
|
+
const ixs = [...additionalIxs, ...setupIxs, ...refreshIntegrationIxs.instructions];
|
|
68929
|
+
const txs = splitInstructionsToFitTransactions([], ixs, {
|
|
68930
|
+
blockhash,
|
|
68931
|
+
payerKey: marginfiAccount.authority,
|
|
68932
|
+
luts: addressLookupTableAccounts ?? []
|
|
68933
|
+
});
|
|
68934
|
+
additionalTxs.push(
|
|
68935
|
+
...txs.map(
|
|
68936
|
+
(tx) => addTransactionMetadata(tx, {
|
|
68937
|
+
type: "CREATE_ATA" /* CREATE_ATA */,
|
|
68938
|
+
addressLookupTables: addressLookupTableAccounts
|
|
68939
|
+
})
|
|
68940
|
+
)
|
|
68941
|
+
);
|
|
68942
|
+
}
|
|
68943
|
+
if (updateFeedIxs.length > 0) {
|
|
68944
|
+
const message = new web3_js.TransactionMessage({
|
|
68945
|
+
payerKey: marginfiAccount.authority,
|
|
68946
|
+
recentBlockhash: blockhash,
|
|
68947
|
+
instructions: updateFeedIxs
|
|
68948
|
+
}).compileToV0Message(feedLuts);
|
|
68949
|
+
additionalTxs.push(
|
|
68950
|
+
addTransactionMetadata(new web3_js.VersionedTransaction(message), {
|
|
68951
|
+
addressLookupTables: feedLuts,
|
|
68952
|
+
type: "CRANK" /* CRANK */
|
|
68953
|
+
})
|
|
68954
|
+
);
|
|
68955
|
+
}
|
|
68956
|
+
const transactions = [...additionalTxs, flashloanTx];
|
|
68957
|
+
return {
|
|
68958
|
+
transactions,
|
|
68959
|
+
actionTxIndex: transactions.length - 1,
|
|
68960
|
+
quoteResponse: swapQuote
|
|
68961
|
+
};
|
|
68962
|
+
}
|
|
68963
|
+
async function buildLoopFlashloanTx(params) {
|
|
68964
|
+
const { depositOpts } = params;
|
|
68965
|
+
const { descriptor, swapNeeded, borrowIxs, depositIxs } = await buildLoopNonSwapIxs(params);
|
|
68966
|
+
if (!swapNeeded) {
|
|
68967
|
+
const flashloanTx2 = await finalizeLoopFlashloanTx({
|
|
68968
|
+
params,
|
|
68969
|
+
innerIxs: descriptor.innerIxs,
|
|
68970
|
+
luts: descriptor.luts,
|
|
68971
|
+
swapIxCount: 0,
|
|
68972
|
+
swapLutCount: 0,
|
|
68973
|
+
sizeConstraint: descriptor.sizeConstraint
|
|
69490
68974
|
});
|
|
68975
|
+
return {
|
|
68976
|
+
flashloanTx: flashloanTx2,
|
|
68977
|
+
setupInstructions: [],
|
|
68978
|
+
swapQuote: void 0,
|
|
68979
|
+
borrowIxs,
|
|
68980
|
+
depositIxs
|
|
68981
|
+
};
|
|
69491
68982
|
}
|
|
69492
|
-
|
|
69493
|
-
|
|
69494
|
-
|
|
69495
|
-
|
|
69496
|
-
|
|
69497
|
-
|
|
69498
|
-
|
|
69499
|
-
|
|
69500
|
-
|
|
69501
|
-
|
|
69502
|
-
|
|
69503
|
-
|
|
69504
|
-
|
|
69505
|
-
|
|
69506
|
-
|
|
69507
|
-
|
|
69508
|
-
|
|
68983
|
+
const engineResult = await runLoopSwapEngine(descriptor, params);
|
|
68984
|
+
const finalIxs = [...descriptor.innerIxs];
|
|
68985
|
+
finalIxs.splice(descriptor.swapSlotIndex, 0, ...engineResult.swapInstructions);
|
|
68986
|
+
const principalNative = depositOpts.loopMode === "DEPOSIT" ? uiToNative(depositOpts.inputDepositAmount, depositOpts.depositBank.mintDecimals) : new BN9__default.default(0);
|
|
68987
|
+
const finalDepositNative = engineResult.outputAmountNative.add(principalNative);
|
|
68988
|
+
const depositIxPosition = descriptor.depositIxIndex + engineResult.swapInstructions.length;
|
|
68989
|
+
patchDepositAmount(finalIxs[depositIxPosition], finalDepositNative);
|
|
68990
|
+
const luts = [...descriptor.luts, ...engineResult.swapLuts];
|
|
68991
|
+
const flashloanTx = await finalizeLoopFlashloanTx({
|
|
68992
|
+
params,
|
|
68993
|
+
innerIxs: finalIxs,
|
|
68994
|
+
luts,
|
|
68995
|
+
swapIxCount: engineResult.swapInstructions.length,
|
|
68996
|
+
swapLutCount: engineResult.swapLuts.length,
|
|
68997
|
+
sizeConstraint: descriptor.sizeConstraint
|
|
68998
|
+
});
|
|
68999
|
+
return {
|
|
69000
|
+
flashloanTx,
|
|
69001
|
+
setupInstructions: engineResult.setupInstructions,
|
|
69002
|
+
swapQuote: engineResult.quoteResponse,
|
|
69003
|
+
borrowIxs,
|
|
69004
|
+
depositIxs
|
|
69005
|
+
};
|
|
69006
|
+
}
|
|
69007
|
+
async function buildLoopNonSwapIxs(params) {
|
|
69008
|
+
const {
|
|
69009
|
+
program,
|
|
69010
|
+
marginfiAccount,
|
|
69011
|
+
bankMap,
|
|
69012
|
+
borrowOpts,
|
|
69013
|
+
depositOpts,
|
|
69014
|
+
bankMetadataMap,
|
|
69015
|
+
addressLookupTableAccounts,
|
|
69016
|
+
overrideInferAccounts
|
|
69017
|
+
} = params;
|
|
69018
|
+
const cuRequestIxs = [
|
|
69019
|
+
web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
|
|
69020
|
+
web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
|
|
69021
|
+
];
|
|
69022
|
+
const swapNeeded = !depositOpts.depositBank.mint.equals(borrowOpts.borrowBank.mint);
|
|
69023
|
+
const destinationTokenAccount = getAssociatedTokenAddressSync(
|
|
69024
|
+
new web3_js.PublicKey(depositOpts.depositBank.mint),
|
|
69025
|
+
marginfiAccount.authority,
|
|
69026
|
+
true,
|
|
69027
|
+
depositOpts.tokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
|
|
69028
|
+
);
|
|
69029
|
+
const principalUi = depositOpts.loopMode === "DEPOSIT" ? depositOpts.inputDepositAmount : 0;
|
|
69030
|
+
let depositAmountUi;
|
|
69031
|
+
let sizeConstraint = 0;
|
|
69032
|
+
let maxSwapTotalAccounts = 0;
|
|
69033
|
+
if (!swapNeeded) {
|
|
69034
|
+
depositAmountUi = borrowOpts.borrowAmount + principalUi;
|
|
69035
|
+
} else {
|
|
69036
|
+
const swapConstraints = await computeFlashloanSwapConstraints({
|
|
69037
|
+
program,
|
|
69038
|
+
marginfiAccount,
|
|
69039
|
+
bankMap,
|
|
69040
|
+
bankMetadataMap,
|
|
69041
|
+
addressLookupTableAccounts: addressLookupTableAccounts ?? [],
|
|
69042
|
+
primaryIx: {
|
|
69043
|
+
type: "borrow",
|
|
69044
|
+
bank: borrowOpts.borrowBank,
|
|
69045
|
+
tokenProgram: borrowOpts.tokenProgram
|
|
69046
|
+
},
|
|
69047
|
+
secondaryIx: {
|
|
69048
|
+
type: "deposit",
|
|
69049
|
+
bank: depositOpts.depositBank,
|
|
69050
|
+
tokenProgram: depositOpts.tokenProgram
|
|
69051
|
+
},
|
|
69052
|
+
overrideInferAccounts
|
|
69509
69053
|
});
|
|
69054
|
+
sizeConstraint = swapConstraints.sizeConstraint;
|
|
69055
|
+
maxSwapTotalAccounts = swapConstraints.maxSwapTotalAccounts;
|
|
69056
|
+
const estimateUi = borrowOpts.borrowAmount * borrowOpts.marketPrice / depositOpts.marketPrice;
|
|
69057
|
+
depositAmountUi = estimateUi + principalUi;
|
|
69510
69058
|
}
|
|
69511
|
-
|
|
69512
|
-
|
|
69513
|
-
|
|
69514
|
-
|
|
69515
|
-
|
|
69516
|
-
|
|
69517
|
-
|
|
69518
|
-
|
|
69519
|
-
|
|
69520
|
-
|
|
69521
|
-
|
|
69522
|
-
|
|
69523
|
-
|
|
69524
|
-
|
|
69525
|
-
|
|
69526
|
-
|
|
69527
|
-
|
|
69528
|
-
|
|
69529
|
-
|
|
69530
|
-
|
|
69531
|
-
|
|
69532
|
-
|
|
69533
|
-
|
|
69534
|
-
|
|
69535
|
-
|
|
69536
|
-
|
|
69537
|
-
|
|
69538
|
-
|
|
69539
|
-
|
|
69540
|
-
|
|
69541
|
-
|
|
69542
|
-
|
|
69543
|
-
|
|
69544
|
-
|
|
69545
|
-
|
|
69546
|
-
|
|
69059
|
+
const borrowIxs = await makeBorrowIx3({
|
|
69060
|
+
program,
|
|
69061
|
+
bank: borrowOpts.borrowBank,
|
|
69062
|
+
bankMap,
|
|
69063
|
+
tokenProgram: borrowOpts.tokenProgram,
|
|
69064
|
+
amount: borrowOpts.borrowAmount,
|
|
69065
|
+
marginfiAccount,
|
|
69066
|
+
authority: marginfiAccount.authority,
|
|
69067
|
+
isSync: false,
|
|
69068
|
+
opts: {
|
|
69069
|
+
createAtas: false,
|
|
69070
|
+
wrapAndUnwrapSol: false,
|
|
69071
|
+
overrideInferAccounts
|
|
69072
|
+
}
|
|
69073
|
+
});
|
|
69074
|
+
const depositIxs = await buildDepositIxs(params, depositAmountUi);
|
|
69075
|
+
const innerIxsBeforeSwap = [...cuRequestIxs, ...borrowIxs.instructions];
|
|
69076
|
+
const swapSlotIndex = innerIxsBeforeSwap.length;
|
|
69077
|
+
const innerIxs = [...innerIxsBeforeSwap, ...depositIxs.instructions];
|
|
69078
|
+
const depositIxIndex = innerIxs.findIndex(isDepositIx);
|
|
69079
|
+
if (depositIxIndex < 0) {
|
|
69080
|
+
throw new Error(
|
|
69081
|
+
"buildLoopNonSwapIxs: could not locate deposit instruction for amount patching"
|
|
69082
|
+
);
|
|
69083
|
+
}
|
|
69084
|
+
const descriptor = {
|
|
69085
|
+
innerIxs,
|
|
69086
|
+
swapSlotIndex,
|
|
69087
|
+
depositIxIndex,
|
|
69088
|
+
inputMint: borrowOpts.borrowBank.mint.toBase58(),
|
|
69089
|
+
outputMint: depositOpts.depositBank.mint.toBase58(),
|
|
69090
|
+
inputDecimals: borrowOpts.borrowBank.mintDecimals,
|
|
69091
|
+
outputDecimals: depositOpts.depositBank.mintDecimals,
|
|
69092
|
+
inAmountNative: uiToNative(
|
|
69093
|
+
borrowOpts.borrowAmount,
|
|
69094
|
+
borrowOpts.borrowBank.mintDecimals
|
|
69095
|
+
).toNumber(),
|
|
69096
|
+
destinationTokenAccount,
|
|
69097
|
+
sizeConstraint,
|
|
69098
|
+
maxSwapTotalAccounts,
|
|
69099
|
+
luts: addressLookupTableAccounts ?? []
|
|
69100
|
+
};
|
|
69101
|
+
return { descriptor, swapNeeded, borrowIxs, depositIxs };
|
|
69102
|
+
}
|
|
69103
|
+
async function buildDepositIxs(params, amountUi) {
|
|
69104
|
+
const { program, marginfiAccount, depositOpts, bankMetadataMap, overrideInferAccounts } = params;
|
|
69105
|
+
switch (depositOpts.depositBank.config.assetTag) {
|
|
69106
|
+
case 3 /* KAMINO */: {
|
|
69107
|
+
const reserve = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.kaminoStates?.reserveState;
|
|
69108
|
+
if (!reserve) {
|
|
69109
|
+
throw TransactionBuildingError.kaminoReserveNotFound(
|
|
69110
|
+
depositOpts.depositBank.address.toBase58(),
|
|
69111
|
+
depositOpts.depositBank.mint.toBase58(),
|
|
69112
|
+
depositOpts.depositBank.tokenSymbol
|
|
69113
|
+
);
|
|
69547
69114
|
}
|
|
69548
|
-
|
|
69115
|
+
return makeKaminoDepositIx3({
|
|
69116
|
+
program,
|
|
69117
|
+
bank: depositOpts.depositBank,
|
|
69118
|
+
tokenProgram: depositOpts.tokenProgram,
|
|
69119
|
+
amount: amountUi,
|
|
69120
|
+
accountAddress: marginfiAccount.address,
|
|
69121
|
+
authority: marginfiAccount.authority,
|
|
69122
|
+
group: marginfiAccount.group,
|
|
69123
|
+
reserve,
|
|
69124
|
+
opts: {
|
|
69125
|
+
wrapAndUnwrapSol: false,
|
|
69126
|
+
overrideInferAccounts
|
|
69127
|
+
}
|
|
69128
|
+
});
|
|
69129
|
+
}
|
|
69130
|
+
case 4 /* DRIFT */: {
|
|
69131
|
+
const driftState = bankMetadataMap[depositOpts.depositBank.address.toBase58()]?.driftStates;
|
|
69132
|
+
if (!driftState) {
|
|
69133
|
+
throw TransactionBuildingError.driftStateNotFound(
|
|
69134
|
+
depositOpts.depositBank.address.toBase58(),
|
|
69135
|
+
depositOpts.depositBank.mint.toBase58(),
|
|
69136
|
+
depositOpts.depositBank.tokenSymbol
|
|
69137
|
+
);
|
|
69138
|
+
}
|
|
69139
|
+
return makeDriftDepositIx3({
|
|
69140
|
+
program,
|
|
69141
|
+
bank: depositOpts.depositBank,
|
|
69142
|
+
tokenProgram: depositOpts.tokenProgram,
|
|
69143
|
+
amount: amountUi,
|
|
69144
|
+
accountAddress: marginfiAccount.address,
|
|
69145
|
+
authority: marginfiAccount.authority,
|
|
69146
|
+
group: marginfiAccount.group,
|
|
69147
|
+
driftMarketIndex: driftState.spotMarketState.marketIndex,
|
|
69148
|
+
driftOracle: driftState.spotMarketState.oracle,
|
|
69149
|
+
opts: {
|
|
69150
|
+
wrapAndUnwrapSol: false,
|
|
69151
|
+
overrideInferAccounts
|
|
69152
|
+
}
|
|
69153
|
+
});
|
|
69154
|
+
}
|
|
69155
|
+
case 6 /* JUPLEND */: {
|
|
69156
|
+
return makeJuplendDepositIx2({
|
|
69157
|
+
program,
|
|
69158
|
+
bank: depositOpts.depositBank,
|
|
69159
|
+
tokenProgram: depositOpts.tokenProgram,
|
|
69160
|
+
amount: amountUi,
|
|
69161
|
+
accountAddress: marginfiAccount.address,
|
|
69162
|
+
authority: marginfiAccount.authority,
|
|
69163
|
+
group: marginfiAccount.group,
|
|
69164
|
+
opts: {
|
|
69165
|
+
wrapAndUnwrapSol: false,
|
|
69166
|
+
overrideInferAccounts
|
|
69167
|
+
}
|
|
69168
|
+
});
|
|
69169
|
+
}
|
|
69170
|
+
default: {
|
|
69171
|
+
return makeDepositIx3({
|
|
69172
|
+
program,
|
|
69173
|
+
bank: depositOpts.depositBank,
|
|
69174
|
+
tokenProgram: depositOpts.tokenProgram,
|
|
69175
|
+
amount: amountUi,
|
|
69176
|
+
accountAddress: marginfiAccount.address,
|
|
69177
|
+
authority: marginfiAccount.authority,
|
|
69178
|
+
group: marginfiAccount.group,
|
|
69179
|
+
opts: {
|
|
69180
|
+
wrapAndUnwrapSol: false,
|
|
69181
|
+
overrideInferAccounts
|
|
69182
|
+
}
|
|
69183
|
+
});
|
|
69184
|
+
}
|
|
69549
69185
|
}
|
|
69550
|
-
|
|
69551
|
-
|
|
69552
|
-
|
|
69553
|
-
|
|
69554
|
-
|
|
69555
|
-
|
|
69556
|
-
|
|
69557
|
-
|
|
69558
|
-
|
|
69559
|
-
|
|
69560
|
-
|
|
69561
|
-
|
|
69562
|
-
|
|
69563
|
-
|
|
69564
|
-
|
|
69186
|
+
}
|
|
69187
|
+
async function runLoopSwapEngine(descriptor, params) {
|
|
69188
|
+
const { connection, swapOpts, marginfiAccount, swapEngineRunner } = params;
|
|
69189
|
+
if (swapOpts.swapIxs) {
|
|
69190
|
+
return {
|
|
69191
|
+
swapInstructions: swapOpts.swapIxs.instructions,
|
|
69192
|
+
setupInstructions: [],
|
|
69193
|
+
swapLuts: swapOpts.swapIxs.lookupTables,
|
|
69194
|
+
quoteResponse: {
|
|
69195
|
+
inAmount: String(descriptor.inAmountNative),
|
|
69196
|
+
outAmount: "0",
|
|
69197
|
+
otherAmountThreshold: "0",
|
|
69198
|
+
slippageBps: 0
|
|
69199
|
+
},
|
|
69200
|
+
outputAmountNative: new BN9__default.default(0)
|
|
69201
|
+
};
|
|
69565
69202
|
}
|
|
69566
|
-
|
|
69567
|
-
|
|
69568
|
-
|
|
69569
|
-
|
|
69570
|
-
|
|
69571
|
-
|
|
69572
|
-
|
|
69573
|
-
|
|
69574
|
-
|
|
69575
|
-
|
|
69576
|
-
|
|
69577
|
-
|
|
69578
|
-
|
|
69579
|
-
|
|
69203
|
+
const runEngine = swapEngineRunner ?? runSwapEngine;
|
|
69204
|
+
const engineResult = await runEngine({
|
|
69205
|
+
inputMint: descriptor.inputMint,
|
|
69206
|
+
outputMint: descriptor.outputMint,
|
|
69207
|
+
amountNative: descriptor.inAmountNative,
|
|
69208
|
+
inputDecimals: descriptor.inputDecimals,
|
|
69209
|
+
outputDecimals: descriptor.outputDecimals,
|
|
69210
|
+
...swapEngineQuoteFieldsFromOpts(swapOpts),
|
|
69211
|
+
taker: marginfiAccount.authority,
|
|
69212
|
+
destinationTokenAccount: descriptor.destinationTokenAccount,
|
|
69213
|
+
connection,
|
|
69214
|
+
footprint: {
|
|
69215
|
+
instructions: descriptor.innerIxs,
|
|
69216
|
+
luts: descriptor.luts,
|
|
69217
|
+
payer: marginfiAccount.authority,
|
|
69218
|
+
sizeConstraint: descriptor.sizeConstraint,
|
|
69219
|
+
maxSwapTotalAccounts: descriptor.maxSwapTotalAccounts
|
|
69220
|
+
},
|
|
69221
|
+
providers: swapEngineProvidersFromOpts(swapOpts)
|
|
69222
|
+
});
|
|
69223
|
+
return {
|
|
69224
|
+
swapInstructions: engineResult.swapInstructions,
|
|
69225
|
+
setupInstructions: engineResult.setupInstructions,
|
|
69226
|
+
swapLuts: engineResult.swapLuts,
|
|
69227
|
+
quoteResponse: engineResult.quoteResponse,
|
|
69228
|
+
outputAmountNative: engineResult.outputAmountNative
|
|
69229
|
+
};
|
|
69230
|
+
}
|
|
69231
|
+
async function finalizeLoopFlashloanTx({
|
|
69232
|
+
params,
|
|
69233
|
+
innerIxs,
|
|
69234
|
+
luts,
|
|
69235
|
+
swapIxCount,
|
|
69236
|
+
swapLutCount,
|
|
69237
|
+
sizeConstraint
|
|
69238
|
+
}) {
|
|
69239
|
+
const { program, marginfiAccount, bankMap, swapOpts, blockhash } = params;
|
|
69240
|
+
if (swapIxCount > 0) {
|
|
69241
|
+
compileFlashloanPrecheck({
|
|
69242
|
+
allIxs: innerIxs,
|
|
69243
|
+
payer: marginfiAccount.authority,
|
|
69244
|
+
luts,
|
|
69245
|
+
sizeConstraint,
|
|
69246
|
+
swapIxCount,
|
|
69247
|
+
swapLutCount
|
|
69580
69248
|
});
|
|
69581
69249
|
}
|
|
69582
|
-
|
|
69583
|
-
|
|
69584
|
-
|
|
69585
|
-
|
|
69586
|
-
|
|
69587
|
-
|
|
69588
|
-
|
|
69589
|
-
|
|
69590
|
-
|
|
69591
|
-
|
|
69592
|
-
|
|
69593
|
-
|
|
69594
|
-
|
|
69595
|
-
|
|
69596
|
-
|
|
69250
|
+
const flashloanTx = await makeFlashLoanTx({
|
|
69251
|
+
program,
|
|
69252
|
+
marginfiAccount,
|
|
69253
|
+
bankMap,
|
|
69254
|
+
addressLookupTableAccounts: luts,
|
|
69255
|
+
blockhash,
|
|
69256
|
+
ixs: innerIxs
|
|
69257
|
+
});
|
|
69258
|
+
const txSize = getTxSize(flashloanTx);
|
|
69259
|
+
const totalKeys = getTotalAccountKeys(flashloanTx);
|
|
69260
|
+
if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
|
|
69261
|
+
throw TransactionBuildingError.swapSizeExceededLoop(
|
|
69262
|
+
txSize,
|
|
69263
|
+
totalKeys,
|
|
69264
|
+
swapOpts.swapConfig?.provider
|
|
69265
|
+
);
|
|
69597
69266
|
}
|
|
69598
|
-
|
|
69599
|
-
|
|
69600
|
-
|
|
69601
|
-
|
|
69602
|
-
|
|
69603
|
-
|
|
69604
|
-
|
|
69605
|
-
|
|
69606
|
-
|
|
69607
|
-
return
|
|
69608
|
-
|
|
69609
|
-
|
|
69610
|
-
|
|
69611
|
-
|
|
69612
|
-
|
|
69613
|
-
|
|
69614
|
-
|
|
69267
|
+
return flashloanTx;
|
|
69268
|
+
}
|
|
69269
|
+
async function makeBridgedLoopTx(params) {
|
|
69270
|
+
const { bridgeOpts, ...loopParams } = params;
|
|
69271
|
+
try {
|
|
69272
|
+
return await makeLoopTx(loopParams);
|
|
69273
|
+
} catch (directError) {
|
|
69274
|
+
if (!isDecomposableSwapError(directError)) throw directError;
|
|
69275
|
+
const bridged = await tryBridgedLoop(loopParams, bridgeOpts);
|
|
69276
|
+
if (bridged) return bridged;
|
|
69277
|
+
throw directError;
|
|
69278
|
+
}
|
|
69279
|
+
}
|
|
69280
|
+
async function tryBridgedLoop(params, bridgeOpts) {
|
|
69281
|
+
const { depositBank } = params.depositOpts;
|
|
69282
|
+
const { borrowBank } = params.borrowOpts;
|
|
69283
|
+
const { usableBridgeBanks, conflictingBridgeBanks } = selectSwapBridges({
|
|
69284
|
+
sourceMint: depositBank.mint,
|
|
69285
|
+
destinationMint: borrowBank.mint,
|
|
69286
|
+
bankMap: params.bankMap,
|
|
69287
|
+
marginfiAccount: params.marginfiAccount,
|
|
69288
|
+
bridgeTokenSide: "borrow",
|
|
69289
|
+
bridgeCandidateMints: bridgeOpts?.bridgeCandidateMints
|
|
69290
|
+
});
|
|
69291
|
+
const oraclePriceOf = (bank) => params.oraclePrices.get(bank.address.toBase58())?.priceRealtime.price.toNumber() ?? 0;
|
|
69292
|
+
const borrowBankPrice = oraclePriceOf(borrowBank);
|
|
69293
|
+
if (borrowBankPrice <= 0) return null;
|
|
69294
|
+
const tokenProgramCache = new Map(bridgeOpts?.tokenProgramByMint);
|
|
69295
|
+
return tryBridgeCandidates({
|
|
69296
|
+
usableBridgeBanks,
|
|
69297
|
+
conflictingBridgeBanks,
|
|
69298
|
+
bridgeTokenSide: "borrow",
|
|
69299
|
+
abortSignal: bridgeOpts?.abortSignal,
|
|
69300
|
+
buildBundleThroughBridge: async (bridgeBank) => {
|
|
69301
|
+
const bridgeBankPrice = oraclePriceOf(bridgeBank);
|
|
69302
|
+
if (bridgeBankPrice <= 0) return null;
|
|
69303
|
+
const bridgeBorrowUi = params.borrowOpts.borrowAmount * borrowBankPrice / bridgeBankPrice;
|
|
69304
|
+
if (bridgeBorrowUi <= 0) return null;
|
|
69305
|
+
const bridgeTokenProgram = await resolveTokenProgramForMint(
|
|
69306
|
+
bridgeBank.mint,
|
|
69307
|
+
params.connection,
|
|
69308
|
+
tokenProgramCache
|
|
69309
|
+
);
|
|
69310
|
+
const firstLeg = await makeLoopTx({
|
|
69311
|
+
...params,
|
|
69312
|
+
depositOpts: {
|
|
69313
|
+
...params.depositOpts,
|
|
69314
|
+
marketPrice: oraclePriceOf(depositBank)
|
|
69315
|
+
},
|
|
69316
|
+
borrowOpts: {
|
|
69317
|
+
borrowAmount: bridgeBorrowUi,
|
|
69318
|
+
borrowBank: bridgeBank,
|
|
69319
|
+
tokenProgram: bridgeTokenProgram,
|
|
69320
|
+
marketPrice: bridgeBankPrice
|
|
69615
69321
|
}
|
|
69322
|
+
});
|
|
69323
|
+
if (!firstLeg.quoteResponse) return null;
|
|
69324
|
+
const result = await composeBridgedSwap({
|
|
69325
|
+
firstLeg,
|
|
69326
|
+
// Second leg: debt-swap the bridge debt → X (repay exactly the bridge the first leg
|
|
69327
|
+
// borrowed — exact, so no slippage residual; repay-all clears it — and borrow X).
|
|
69328
|
+
buildSecondLeg: (projectedAccount) => makeSwapDebtTx({
|
|
69329
|
+
...sharedBridgeLegContext(params),
|
|
69330
|
+
marginfiAccount: projectedAccount,
|
|
69331
|
+
repayOpts: {
|
|
69332
|
+
totalPositionAmount: bridgeBorrowUi,
|
|
69333
|
+
repayAmount: bridgeBorrowUi,
|
|
69334
|
+
repayBank: bridgeBank,
|
|
69335
|
+
tokenProgram: bridgeTokenProgram,
|
|
69336
|
+
marketPrice: bridgeBankPrice
|
|
69337
|
+
},
|
|
69338
|
+
borrowOpts: {
|
|
69339
|
+
borrowBank,
|
|
69340
|
+
tokenProgram: params.borrowOpts.tokenProgram,
|
|
69341
|
+
marketPrice: borrowBankPrice
|
|
69342
|
+
}
|
|
69343
|
+
}),
|
|
69344
|
+
marginfiAccount: params.marginfiAccount,
|
|
69345
|
+
program: params.program,
|
|
69346
|
+
banksMap: params.bankMap,
|
|
69347
|
+
assetShareValueMultiplierByBank: params.assetShareValueMultiplierByBank,
|
|
69348
|
+
feePayer: params.overrideInferAccounts?.authority ?? params.marginfiAccount.authority,
|
|
69349
|
+
maxBundleTxs: bridgeOpts?.maxBundleTxs
|
|
69350
|
+
});
|
|
69351
|
+
if (!result) return null;
|
|
69352
|
+
return {
|
|
69353
|
+
transactions: result.transactions,
|
|
69354
|
+
actionTxIndex: result.transactions.length - 1,
|
|
69355
|
+
quoteResponse: mergeBridgeQuotesLoop(result.firstLegQuote, result.secondLegQuote),
|
|
69356
|
+
bridgeMint: bridgeBank.mint
|
|
69357
|
+
};
|
|
69358
|
+
}
|
|
69359
|
+
});
|
|
69360
|
+
}
|
|
69361
|
+
async function makeSwapCollateralTx(params) {
|
|
69362
|
+
const {
|
|
69363
|
+
program,
|
|
69364
|
+
marginfiAccount,
|
|
69365
|
+
connection,
|
|
69366
|
+
bankMap,
|
|
69367
|
+
oraclePrices,
|
|
69368
|
+
withdrawOpts,
|
|
69369
|
+
depositOpts,
|
|
69370
|
+
bankMetadataMap,
|
|
69371
|
+
assetShareValueMultiplierByBank,
|
|
69372
|
+
addressLookupTableAccounts,
|
|
69373
|
+
crossbarUrl,
|
|
69374
|
+
additionalIxs = []
|
|
69375
|
+
} = params;
|
|
69376
|
+
const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
|
|
69377
|
+
const setupIxs = await makeSetupIx({
|
|
69378
|
+
connection,
|
|
69379
|
+
authority: marginfiAccount.authority,
|
|
69380
|
+
tokens: [
|
|
69381
|
+
{ mint: withdrawOpts.withdrawBank.mint, tokenProgram: withdrawOpts.tokenProgram },
|
|
69382
|
+
{ mint: depositOpts.depositBank.mint, tokenProgram: depositOpts.tokenProgram }
|
|
69383
|
+
]
|
|
69384
|
+
});
|
|
69385
|
+
const refreshIntegrationIxs = makeRefreshIntegrationBanksIxs(
|
|
69386
|
+
marginfiAccount,
|
|
69387
|
+
bankMap,
|
|
69388
|
+
[withdrawOpts.withdrawBank.address, depositOpts.depositBank.address],
|
|
69389
|
+
bankMetadataMap
|
|
69390
|
+
);
|
|
69391
|
+
const { flashloanTx, setupInstructions, swapQuote, withdrawIxs, depositIxs } = await buildSwapCollateralFlashloanTx({
|
|
69392
|
+
...params,
|
|
69393
|
+
blockhash
|
|
69394
|
+
});
|
|
69395
|
+
const jupiterSetupInstructions = setupInstructions.filter((ix) => {
|
|
69396
|
+
if (ix.programId.equals(web3_js.ComputeBudgetProgram.programId)) {
|
|
69397
|
+
return false;
|
|
69398
|
+
}
|
|
69399
|
+
if (ix.programId.equals(ASSOCIATED_TOKEN_PROGRAM_ID)) {
|
|
69400
|
+
const mintKey = ix.keys[3]?.pubkey;
|
|
69401
|
+
if (mintKey?.equals(withdrawOpts.withdrawBank.mint) || mintKey?.equals(depositOpts.depositBank.mint)) {
|
|
69402
|
+
return false;
|
|
69616
69403
|
}
|
|
69404
|
+
}
|
|
69405
|
+
return true;
|
|
69406
|
+
});
|
|
69407
|
+
setupIxs.push(...jupiterSetupInstructions);
|
|
69408
|
+
const { instructions: updateFeedIxs, luts: feedLuts } = await makeSmartCrankSwbFeedIx({
|
|
69409
|
+
marginfiAccount,
|
|
69410
|
+
bankMap,
|
|
69411
|
+
oraclePrices,
|
|
69412
|
+
assetShareValueMultiplierByBank,
|
|
69413
|
+
instructions: [...withdrawIxs.instructions, ...depositIxs.instructions],
|
|
69414
|
+
program,
|
|
69415
|
+
connection,
|
|
69416
|
+
crossbarUrl
|
|
69417
|
+
});
|
|
69418
|
+
let additionalTxs = [];
|
|
69419
|
+
if (setupIxs.length > 0 || refreshIntegrationIxs.instructions.length > 0) {
|
|
69420
|
+
const ixs = [...setupIxs, ...refreshIntegrationIxs.instructions];
|
|
69421
|
+
const txs = splitInstructionsToFitTransactions([], ixs, {
|
|
69422
|
+
blockhash,
|
|
69423
|
+
payerKey: marginfiAccount.authority,
|
|
69424
|
+
luts: addressLookupTableAccounts ?? []
|
|
69617
69425
|
});
|
|
69426
|
+
additionalTxs.push(
|
|
69427
|
+
...txs.map(
|
|
69428
|
+
(tx) => addTransactionMetadata(tx, {
|
|
69429
|
+
type: "CREATE_ATA" /* CREATE_ATA */,
|
|
69430
|
+
addressLookupTables: addressLookupTableAccounts
|
|
69431
|
+
})
|
|
69432
|
+
)
|
|
69433
|
+
);
|
|
69618
69434
|
}
|
|
69619
|
-
|
|
69620
|
-
|
|
69621
|
-
|
|
69622
|
-
|
|
69623
|
-
|
|
69624
|
-
|
|
69625
|
-
|
|
69626
|
-
|
|
69627
|
-
|
|
69628
|
-
|
|
69629
|
-
|
|
69630
|
-
|
|
69631
|
-
authority: this.authority
|
|
69632
|
-
});
|
|
69435
|
+
if (updateFeedIxs.length > 0) {
|
|
69436
|
+
const message = new web3_js.TransactionMessage({
|
|
69437
|
+
payerKey: marginfiAccount.authority,
|
|
69438
|
+
recentBlockhash: blockhash,
|
|
69439
|
+
instructions: updateFeedIxs
|
|
69440
|
+
}).compileToV0Message(feedLuts);
|
|
69441
|
+
additionalTxs.push(
|
|
69442
|
+
addTransactionMetadata(new web3_js.VersionedTransaction(message), {
|
|
69443
|
+
addressLookupTables: feedLuts,
|
|
69444
|
+
type: "CRANK" /* CRANK */
|
|
69445
|
+
})
|
|
69446
|
+
);
|
|
69633
69447
|
}
|
|
69634
|
-
|
|
69635
|
-
|
|
69636
|
-
|
|
69637
|
-
|
|
69638
|
-
|
|
69639
|
-
|
|
69640
|
-
|
|
69641
|
-
|
|
69642
|
-
|
|
69643
|
-
|
|
69644
|
-
|
|
69645
|
-
|
|
69646
|
-
|
|
69647
|
-
|
|
69648
|
-
|
|
69649
|
-
|
|
69650
|
-
|
|
69651
|
-
|
|
69448
|
+
const transactions = [...additionalTxs, flashloanTx];
|
|
69449
|
+
return {
|
|
69450
|
+
transactions,
|
|
69451
|
+
actionTxIndex: transactions.length - 1,
|
|
69452
|
+
quoteResponse: swapQuote
|
|
69453
|
+
};
|
|
69454
|
+
}
|
|
69455
|
+
async function buildSwapCollateralFlashloanTx({
|
|
69456
|
+
program,
|
|
69457
|
+
marginfiAccount,
|
|
69458
|
+
bankMap,
|
|
69459
|
+
withdrawOpts,
|
|
69460
|
+
depositOpts,
|
|
69461
|
+
swapOpts,
|
|
69462
|
+
bankMetadataMap,
|
|
69463
|
+
assetShareValueMultiplierByBank,
|
|
69464
|
+
addressLookupTableAccounts,
|
|
69465
|
+
connection,
|
|
69466
|
+
overrideInferAccounts,
|
|
69467
|
+
blockhash,
|
|
69468
|
+
swapEngineRunner
|
|
69469
|
+
}) {
|
|
69470
|
+
const {
|
|
69471
|
+
withdrawBank,
|
|
69472
|
+
tokenProgram: withdrawTokenProgram,
|
|
69473
|
+
totalPositionAmount,
|
|
69474
|
+
withdrawAmount
|
|
69475
|
+
} = withdrawOpts;
|
|
69476
|
+
const { depositBank, tokenProgram: depositTokenProgram } = depositOpts;
|
|
69477
|
+
if (withdrawAmount !== void 0 && withdrawAmount <= 0) {
|
|
69478
|
+
throw new Error("withdrawAmount must be greater than 0");
|
|
69479
|
+
}
|
|
69480
|
+
const actualWithdrawAmount = Math.min(withdrawAmount ?? totalPositionAmount, totalPositionAmount);
|
|
69481
|
+
const isFullWithdraw = isWholePosition(
|
|
69482
|
+
{ amount: totalPositionAmount, isLending: true },
|
|
69483
|
+
actualWithdrawAmount,
|
|
69484
|
+
withdrawBank.mintDecimals
|
|
69485
|
+
);
|
|
69486
|
+
const cuRequestIxs = [
|
|
69487
|
+
web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
|
|
69488
|
+
web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
|
|
69489
|
+
];
|
|
69490
|
+
let amountToDeposit;
|
|
69491
|
+
let swapInstructions = [];
|
|
69492
|
+
let setupInstructions = [];
|
|
69493
|
+
let swapLookupTables = [];
|
|
69494
|
+
let swapQuote;
|
|
69495
|
+
let sizeConstraintUsed = 0;
|
|
69496
|
+
let withdrawIxs;
|
|
69497
|
+
switch (withdrawOpts.withdrawBank.config.assetTag) {
|
|
69498
|
+
case 3 /* KAMINO */: {
|
|
69499
|
+
const reserve = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.kaminoStates?.reserveState;
|
|
69500
|
+
if (!reserve) {
|
|
69501
|
+
throw TransactionBuildingError.kaminoReserveNotFound(
|
|
69502
|
+
withdrawOpts.withdrawBank.address.toBase58(),
|
|
69503
|
+
withdrawOpts.withdrawBank.mint.toBase58(),
|
|
69504
|
+
withdrawOpts.withdrawBank.tokenSymbol
|
|
69505
|
+
);
|
|
69506
|
+
}
|
|
69507
|
+
const multiplier = assetShareValueMultiplierByBank.get(withdrawOpts.withdrawBank.address.toBase58()) ?? new BigNumber3.BigNumber(1);
|
|
69508
|
+
const adjustedAmount = new BigNumber3.BigNumber(actualWithdrawAmount).div(multiplier).times(1.0001).toNumber();
|
|
69509
|
+
withdrawIxs = await makeKaminoWithdrawIx3({
|
|
69510
|
+
program,
|
|
69511
|
+
bank: withdrawBank,
|
|
69512
|
+
bankMap,
|
|
69513
|
+
tokenProgram: withdrawTokenProgram,
|
|
69514
|
+
cTokenAmount: adjustedAmount,
|
|
69515
|
+
marginfiAccount,
|
|
69516
|
+
authority: marginfiAccount.authority,
|
|
69517
|
+
reserve,
|
|
69518
|
+
withdrawAll: isFullWithdraw,
|
|
69519
|
+
isSync: false,
|
|
69520
|
+
opts: {
|
|
69521
|
+
createAtas: false,
|
|
69522
|
+
wrapAndUnwrapSol: false,
|
|
69523
|
+
overrideInferAccounts
|
|
69652
69524
|
}
|
|
69525
|
+
});
|
|
69526
|
+
break;
|
|
69527
|
+
}
|
|
69528
|
+
case 4 /* DRIFT */: {
|
|
69529
|
+
const driftState = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.driftStates;
|
|
69530
|
+
if (!driftState) {
|
|
69531
|
+
throw TransactionBuildingError.driftStateNotFound(
|
|
69532
|
+
withdrawOpts.withdrawBank.address.toBase58(),
|
|
69533
|
+
withdrawOpts.withdrawBank.mint.toBase58(),
|
|
69534
|
+
withdrawOpts.withdrawBank.tokenSymbol
|
|
69535
|
+
);
|
|
69653
69536
|
}
|
|
69654
|
-
|
|
69655
|
-
|
|
69656
|
-
|
|
69657
|
-
|
|
69658
|
-
|
|
69659
|
-
|
|
69660
|
-
|
|
69661
|
-
|
|
69662
|
-
|
|
69663
|
-
|
|
69664
|
-
|
|
69665
|
-
|
|
69666
|
-
|
|
69667
|
-
|
|
69668
|
-
|
|
69669
|
-
|
|
69670
|
-
overrideInferAccounts: {
|
|
69671
|
-
authority: this.authority,
|
|
69672
|
-
group: this.group,
|
|
69673
|
-
...params.opts?.overrideInferAccounts
|
|
69537
|
+
withdrawIxs = await makeDriftWithdrawIx3({
|
|
69538
|
+
program,
|
|
69539
|
+
bank: withdrawOpts.withdrawBank,
|
|
69540
|
+
bankMap,
|
|
69541
|
+
tokenProgram: withdrawOpts.tokenProgram,
|
|
69542
|
+
amount: actualWithdrawAmount,
|
|
69543
|
+
marginfiAccount,
|
|
69544
|
+
authority: marginfiAccount.authority,
|
|
69545
|
+
driftSpotMarket: driftState.spotMarketState,
|
|
69546
|
+
userRewards: driftState.userRewards,
|
|
69547
|
+
withdrawAll: isFullWithdraw,
|
|
69548
|
+
isSync: false,
|
|
69549
|
+
opts: {
|
|
69550
|
+
createAtas: false,
|
|
69551
|
+
wrapAndUnwrapSol: false,
|
|
69552
|
+
overrideInferAccounts
|
|
69674
69553
|
}
|
|
69554
|
+
});
|
|
69555
|
+
break;
|
|
69556
|
+
}
|
|
69557
|
+
case 6 /* JUPLEND */: {
|
|
69558
|
+
const jupLendState = bankMetadataMap[withdrawOpts.withdrawBank.address.toBase58()]?.jupLendStates;
|
|
69559
|
+
if (!jupLendState) {
|
|
69560
|
+
throw TransactionBuildingError.jupLendStateNotFound(
|
|
69561
|
+
withdrawOpts.withdrawBank.address.toBase58(),
|
|
69562
|
+
withdrawOpts.withdrawBank.mint.toBase58(),
|
|
69563
|
+
withdrawOpts.withdrawBank.tokenSymbol
|
|
69564
|
+
);
|
|
69675
69565
|
}
|
|
69676
|
-
|
|
69566
|
+
withdrawIxs = await makeJuplendWithdrawIx2({
|
|
69567
|
+
program,
|
|
69568
|
+
bank: withdrawBank,
|
|
69569
|
+
bankMap,
|
|
69570
|
+
tokenProgram: withdrawTokenProgram,
|
|
69571
|
+
amount: actualWithdrawAmount,
|
|
69572
|
+
marginfiAccount,
|
|
69573
|
+
authority: marginfiAccount.authority,
|
|
69574
|
+
jupLendingState: jupLendState.jupLendingState,
|
|
69575
|
+
withdrawAll: isFullWithdraw,
|
|
69576
|
+
opts: {
|
|
69577
|
+
createAtas: false,
|
|
69578
|
+
wrapAndUnwrapSol: false,
|
|
69579
|
+
overrideInferAccounts
|
|
69580
|
+
}
|
|
69581
|
+
});
|
|
69582
|
+
break;
|
|
69583
|
+
}
|
|
69584
|
+
default: {
|
|
69585
|
+
withdrawIxs = await makeWithdrawIx3({
|
|
69586
|
+
program,
|
|
69587
|
+
bank: withdrawBank,
|
|
69588
|
+
bankMap,
|
|
69589
|
+
tokenProgram: withdrawTokenProgram,
|
|
69590
|
+
amount: actualWithdrawAmount,
|
|
69591
|
+
marginfiAccount,
|
|
69592
|
+
authority: marginfiAccount.authority,
|
|
69593
|
+
withdrawAll: isFullWithdraw,
|
|
69594
|
+
isSync: false,
|
|
69595
|
+
opts: {
|
|
69596
|
+
createAtas: false,
|
|
69597
|
+
wrapAndUnwrapSol: false,
|
|
69598
|
+
overrideInferAccounts
|
|
69599
|
+
}
|
|
69600
|
+
});
|
|
69601
|
+
break;
|
|
69602
|
+
}
|
|
69677
69603
|
}
|
|
69678
|
-
|
|
69679
|
-
|
|
69680
|
-
|
|
69681
|
-
|
|
69682
|
-
|
|
69683
|
-
|
|
69684
|
-
|
|
69685
|
-
|
|
69686
|
-
|
|
69687
|
-
|
|
69688
|
-
|
|
69689
|
-
|
|
69690
|
-
|
|
69691
|
-
|
|
69692
|
-
|
|
69693
|
-
|
|
69694
|
-
|
|
69695
|
-
|
|
69604
|
+
const swapNeeded = !depositBank.mint.equals(withdrawBank.mint);
|
|
69605
|
+
amountToDeposit = swapNeeded ? 0 : actualWithdrawAmount;
|
|
69606
|
+
let depositIxs;
|
|
69607
|
+
switch (depositBank.config.assetTag) {
|
|
69608
|
+
case 3 /* KAMINO */: {
|
|
69609
|
+
const reserve = bankMetadataMap[depositBank.address.toBase58()]?.kaminoStates?.reserveState;
|
|
69610
|
+
if (!reserve) {
|
|
69611
|
+
throw TransactionBuildingError.kaminoReserveNotFound(
|
|
69612
|
+
depositBank.address.toBase58(),
|
|
69613
|
+
depositBank.mint.toBase58(),
|
|
69614
|
+
depositBank.tokenSymbol
|
|
69615
|
+
);
|
|
69616
|
+
}
|
|
69617
|
+
depositIxs = await makeKaminoDepositIx3({
|
|
69618
|
+
program,
|
|
69619
|
+
bank: depositBank,
|
|
69620
|
+
tokenProgram: depositTokenProgram,
|
|
69621
|
+
amount: amountToDeposit,
|
|
69622
|
+
accountAddress: marginfiAccount.address,
|
|
69623
|
+
authority: marginfiAccount.authority,
|
|
69624
|
+
group: marginfiAccount.group,
|
|
69625
|
+
reserve,
|
|
69626
|
+
opts: {
|
|
69627
|
+
wrapAndUnwrapSol: false,
|
|
69628
|
+
overrideInferAccounts
|
|
69696
69629
|
}
|
|
69630
|
+
});
|
|
69631
|
+
break;
|
|
69632
|
+
}
|
|
69633
|
+
case 4 /* DRIFT */: {
|
|
69634
|
+
const driftState = bankMetadataMap[depositBank.address.toBase58()]?.driftStates;
|
|
69635
|
+
if (!driftState) {
|
|
69636
|
+
throw TransactionBuildingError.driftStateNotFound(
|
|
69637
|
+
depositBank.address.toBase58(),
|
|
69638
|
+
depositBank.mint.toBase58(),
|
|
69639
|
+
depositBank.tokenSymbol
|
|
69640
|
+
);
|
|
69697
69641
|
}
|
|
69642
|
+
const driftMarketIndex = driftState.spotMarketState.marketIndex;
|
|
69643
|
+
const driftOracle = driftState.spotMarketState.oracle;
|
|
69644
|
+
depositIxs = await makeDriftDepositIx3({
|
|
69645
|
+
program,
|
|
69646
|
+
bank: depositBank,
|
|
69647
|
+
tokenProgram: depositTokenProgram,
|
|
69648
|
+
amount: amountToDeposit,
|
|
69649
|
+
accountAddress: marginfiAccount.address,
|
|
69650
|
+
authority: marginfiAccount.authority,
|
|
69651
|
+
group: marginfiAccount.group,
|
|
69652
|
+
driftMarketIndex,
|
|
69653
|
+
driftOracle,
|
|
69654
|
+
opts: {
|
|
69655
|
+
wrapAndUnwrapSol: false,
|
|
69656
|
+
overrideInferAccounts
|
|
69657
|
+
}
|
|
69658
|
+
});
|
|
69659
|
+
break;
|
|
69660
|
+
}
|
|
69661
|
+
case 6 /* JUPLEND */: {
|
|
69662
|
+
depositIxs = await makeJuplendDepositIx2({
|
|
69663
|
+
program,
|
|
69664
|
+
bank: depositBank,
|
|
69665
|
+
tokenProgram: depositTokenProgram,
|
|
69666
|
+
amount: amountToDeposit,
|
|
69667
|
+
accountAddress: marginfiAccount.address,
|
|
69668
|
+
authority: marginfiAccount.authority,
|
|
69669
|
+
group: marginfiAccount.group,
|
|
69670
|
+
opts: {
|
|
69671
|
+
wrapAndUnwrapSol: false,
|
|
69672
|
+
overrideInferAccounts
|
|
69673
|
+
}
|
|
69674
|
+
});
|
|
69675
|
+
break;
|
|
69676
|
+
}
|
|
69677
|
+
default: {
|
|
69678
|
+
depositIxs = await makeDepositIx3({
|
|
69679
|
+
program,
|
|
69680
|
+
bank: depositBank,
|
|
69681
|
+
tokenProgram: depositTokenProgram,
|
|
69682
|
+
amount: amountToDeposit,
|
|
69683
|
+
accountAddress: marginfiAccount.address,
|
|
69684
|
+
authority: marginfiAccount.authority,
|
|
69685
|
+
group: marginfiAccount.group,
|
|
69686
|
+
opts: {
|
|
69687
|
+
wrapAndUnwrapSol: false,
|
|
69688
|
+
overrideInferAccounts
|
|
69689
|
+
}
|
|
69690
|
+
});
|
|
69691
|
+
break;
|
|
69692
|
+
}
|
|
69693
|
+
}
|
|
69694
|
+
if (swapNeeded) {
|
|
69695
|
+
const destinationTokenAccount = getAssociatedTokenAddressSync(
|
|
69696
|
+
depositBank.mint,
|
|
69697
|
+
marginfiAccount.authority,
|
|
69698
|
+
true,
|
|
69699
|
+
depositTokenProgram.equals(TOKEN_2022_PROGRAM_ID) ? TOKEN_2022_PROGRAM_ID : void 0
|
|
69700
|
+
);
|
|
69701
|
+
const swapConstraints = await computeFlashloanSwapConstraints({
|
|
69702
|
+
program,
|
|
69703
|
+
marginfiAccount,
|
|
69704
|
+
bankMap,
|
|
69705
|
+
bankMetadataMap,
|
|
69706
|
+
addressLookupTableAccounts: addressLookupTableAccounts ?? [],
|
|
69707
|
+
primaryIx: { type: "withdraw", bank: withdrawBank, tokenProgram: withdrawTokenProgram },
|
|
69708
|
+
secondaryIx: { type: "deposit", bank: depositBank, tokenProgram: depositTokenProgram },
|
|
69709
|
+
overrideInferAccounts
|
|
69710
|
+
});
|
|
69711
|
+
sizeConstraintUsed = swapConstraints.sizeConstraint;
|
|
69712
|
+
const runEngine = swapEngineRunner ?? runSwapEngine;
|
|
69713
|
+
const engineResult = await runEngine({
|
|
69714
|
+
inputMint: withdrawBank.mint.toBase58(),
|
|
69715
|
+
outputMint: depositBank.mint.toBase58(),
|
|
69716
|
+
amountNative: uiToNative(actualWithdrawAmount, withdrawBank.mintDecimals).toNumber(),
|
|
69717
|
+
inputDecimals: withdrawBank.mintDecimals,
|
|
69718
|
+
outputDecimals: depositBank.mintDecimals,
|
|
69719
|
+
...swapEngineQuoteFieldsFromOpts(swapOpts),
|
|
69720
|
+
taker: marginfiAccount.authority,
|
|
69721
|
+
destinationTokenAccount,
|
|
69722
|
+
connection,
|
|
69723
|
+
footprint: {
|
|
69724
|
+
instructions: [...cuRequestIxs, ...withdrawIxs.instructions, ...depositIxs.instructions],
|
|
69725
|
+
luts: addressLookupTableAccounts ?? [],
|
|
69726
|
+
payer: marginfiAccount.authority,
|
|
69727
|
+
sizeConstraint: swapConstraints.sizeConstraint,
|
|
69728
|
+
maxSwapTotalAccounts: swapConstraints.maxSwapTotalAccounts
|
|
69729
|
+
},
|
|
69730
|
+
providers: swapEngineProvidersFromOpts(swapOpts)
|
|
69698
69731
|
});
|
|
69732
|
+
const depositIxToPatch = depositIxs.instructions.find(isDepositIx);
|
|
69733
|
+
if (!depositIxToPatch) {
|
|
69734
|
+
throw new Error("swap-collateral: could not locate deposit instruction for amount patching");
|
|
69735
|
+
}
|
|
69736
|
+
patchDepositAmount(depositIxToPatch, engineResult.outputAmountNative);
|
|
69737
|
+
swapInstructions = engineResult.swapInstructions;
|
|
69738
|
+
setupInstructions = engineResult.setupInstructions;
|
|
69739
|
+
swapLookupTables = engineResult.swapLuts;
|
|
69740
|
+
swapQuote = engineResult.quoteResponse;
|
|
69699
69741
|
}
|
|
69700
|
-
|
|
69701
|
-
|
|
69702
|
-
|
|
69703
|
-
|
|
69704
|
-
|
|
69705
|
-
|
|
69706
|
-
|
|
69707
|
-
|
|
69708
|
-
|
|
69709
|
-
|
|
69710
|
-
|
|
69711
|
-
|
|
69742
|
+
const luts = [...addressLookupTableAccounts ?? [], ...swapLookupTables];
|
|
69743
|
+
const allNonFlIxs = [
|
|
69744
|
+
...cuRequestIxs,
|
|
69745
|
+
...withdrawIxs.instructions,
|
|
69746
|
+
...swapInstructions,
|
|
69747
|
+
...depositIxs.instructions
|
|
69748
|
+
];
|
|
69749
|
+
if (swapInstructions.length > 0) {
|
|
69750
|
+
compileFlashloanPrecheck({
|
|
69751
|
+
allIxs: allNonFlIxs,
|
|
69752
|
+
payer: marginfiAccount.authority,
|
|
69753
|
+
luts,
|
|
69754
|
+
sizeConstraint: sizeConstraintUsed,
|
|
69755
|
+
swapIxCount: swapInstructions.length,
|
|
69756
|
+
swapLutCount: swapLookupTables.length
|
|
69757
|
+
});
|
|
69758
|
+
}
|
|
69759
|
+
const flashloanTx = await makeFlashLoanTx({
|
|
69760
|
+
program,
|
|
69761
|
+
marginfiAccount,
|
|
69762
|
+
bankMap,
|
|
69763
|
+
addressLookupTableAccounts: luts,
|
|
69764
|
+
blockhash,
|
|
69765
|
+
ixs: allNonFlIxs,
|
|
69766
|
+
isSync: false
|
|
69767
|
+
});
|
|
69768
|
+
const txSize = getTxSize(flashloanTx);
|
|
69769
|
+
const totalKeys = getTotalAccountKeys(flashloanTx);
|
|
69770
|
+
if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
|
|
69771
|
+
throw TransactionBuildingError.swapSizeExceededPositionSwap(
|
|
69772
|
+
txSize,
|
|
69773
|
+
totalKeys,
|
|
69774
|
+
swapOpts.swapConfig?.provider
|
|
69775
|
+
);
|
|
69776
|
+
}
|
|
69777
|
+
return {
|
|
69778
|
+
flashloanTx,
|
|
69779
|
+
setupInstructions,
|
|
69780
|
+
swapQuote,
|
|
69781
|
+
withdrawIxs,
|
|
69782
|
+
depositIxs
|
|
69783
|
+
};
|
|
69784
|
+
}
|
|
69785
|
+
var SECOND_LEG_ROUNDING_HEADROOM_NATIVE = 10;
|
|
69786
|
+
async function makeBridgedSwapCollateralTx(params) {
|
|
69787
|
+
const { bridgeOpts, ...directParams } = params;
|
|
69788
|
+
try {
|
|
69789
|
+
return await makeSwapCollateralTx(directParams);
|
|
69790
|
+
} catch (directError) {
|
|
69791
|
+
if (!isDecomposableSwapError(directError)) throw directError;
|
|
69792
|
+
const bridged = await tryBridgedCollateralSwap(directParams, bridgeOpts);
|
|
69793
|
+
if (bridged) return bridged;
|
|
69794
|
+
throw directError;
|
|
69795
|
+
}
|
|
69796
|
+
}
|
|
69797
|
+
async function tryBridgedCollateralSwap(params, bridgeOpts) {
|
|
69798
|
+
const sourceBank = params.withdrawOpts.withdrawBank;
|
|
69799
|
+
const destinationBank = params.depositOpts.depositBank;
|
|
69800
|
+
const withdrawAmount = params.withdrawOpts.withdrawAmount ?? params.withdrawOpts.totalPositionAmount;
|
|
69801
|
+
const { usableBridgeBanks, conflictingBridgeBanks } = selectSwapBridges({
|
|
69802
|
+
sourceMint: sourceBank.mint,
|
|
69803
|
+
destinationMint: destinationBank.mint,
|
|
69804
|
+
bankMap: params.bankMap,
|
|
69805
|
+
marginfiAccount: params.marginfiAccount,
|
|
69806
|
+
bridgeTokenSide: "deposit",
|
|
69807
|
+
bridgeCandidateMints: bridgeOpts?.bridgeCandidateMints
|
|
69808
|
+
});
|
|
69809
|
+
const tokenProgramCache = new Map(bridgeOpts?.tokenProgramByMint);
|
|
69810
|
+
return tryBridgeCandidates({
|
|
69811
|
+
usableBridgeBanks,
|
|
69812
|
+
conflictingBridgeBanks,
|
|
69813
|
+
bridgeTokenSide: "deposit",
|
|
69814
|
+
abortSignal: bridgeOpts?.abortSignal,
|
|
69815
|
+
buildBundleThroughBridge: async (bridgeBank) => {
|
|
69816
|
+
const bridgeTokenProgram = await resolveTokenProgramForMint(
|
|
69817
|
+
bridgeBank.mint,
|
|
69818
|
+
params.connection,
|
|
69819
|
+
tokenProgramCache
|
|
69820
|
+
);
|
|
69821
|
+
const firstLeg = await makeSwapCollateralTx({
|
|
69822
|
+
...sharedBridgeLegContext(params),
|
|
69823
|
+
withdrawOpts: {
|
|
69824
|
+
totalPositionAmount: params.withdrawOpts.totalPositionAmount,
|
|
69825
|
+
withdrawAmount,
|
|
69826
|
+
withdrawBank: sourceBank,
|
|
69827
|
+
tokenProgram: params.withdrawOpts.tokenProgram
|
|
69828
|
+
},
|
|
69829
|
+
depositOpts: { depositBank: bridgeBank, tokenProgram: bridgeTokenProgram }
|
|
69830
|
+
});
|
|
69831
|
+
if (!firstLeg.quoteResponse) return null;
|
|
69832
|
+
const bridgeMinOutNative = Number(firstLeg.quoteResponse.otherAmountThreshold);
|
|
69833
|
+
const secondLegAmountNative = bridgeMinOutNative - SECOND_LEG_ROUNDING_HEADROOM_NATIVE;
|
|
69834
|
+
if (secondLegAmountNative <= 0) return null;
|
|
69835
|
+
const secondLegAmountUi = nativeToUi(secondLegAmountNative, bridgeBank.mintDecimals);
|
|
69836
|
+
const hasBridgeDeposit = params.marginfiAccount.balances.some(
|
|
69837
|
+
(b) => b.active && b.bankPk.equals(bridgeBank.address) && b.assetShares.gt(0)
|
|
69838
|
+
);
|
|
69839
|
+
const result = await composeBridgedSwap({
|
|
69840
|
+
firstLeg,
|
|
69841
|
+
// The second leg builds against the first leg's projected effect.
|
|
69842
|
+
buildSecondLeg: (projectedAccount) => makeSwapCollateralTx({
|
|
69843
|
+
...sharedBridgeLegContext(params),
|
|
69844
|
+
marginfiAccount: projectedAccount,
|
|
69845
|
+
withdrawOpts: {
|
|
69846
|
+
totalPositionAmount: hasBridgeDeposit ? nativeToUi(bridgeMinOutNative, bridgeBank.mintDecimals) : secondLegAmountUi,
|
|
69847
|
+
withdrawAmount: secondLegAmountUi,
|
|
69848
|
+
withdrawBank: bridgeBank,
|
|
69849
|
+
tokenProgram: bridgeTokenProgram
|
|
69850
|
+
},
|
|
69851
|
+
depositOpts: params.depositOpts
|
|
69852
|
+
}),
|
|
69853
|
+
marginfiAccount: params.marginfiAccount,
|
|
69854
|
+
program: params.program,
|
|
69855
|
+
banksMap: params.bankMap,
|
|
69856
|
+
assetShareValueMultiplierByBank: params.assetShareValueMultiplierByBank,
|
|
69857
|
+
feePayer: params.overrideInferAccounts?.authority ?? params.marginfiAccount.authority,
|
|
69858
|
+
maxBundleTxs: bridgeOpts?.maxBundleTxs
|
|
69859
|
+
});
|
|
69860
|
+
if (!result) return null;
|
|
69861
|
+
return {
|
|
69862
|
+
transactions: result.transactions,
|
|
69863
|
+
actionTxIndex: result.transactions.length - 1,
|
|
69864
|
+
quoteResponse: mergeBridgeQuotes(result.firstLegQuote, result.secondLegQuote),
|
|
69865
|
+
bridgeMint: bridgeBank.mint
|
|
69866
|
+
};
|
|
69867
|
+
}
|
|
69868
|
+
});
|
|
69869
|
+
}
|
|
69870
|
+
var DEFAULT_ROLL_SLIPPAGE_BPS = 50;
|
|
69871
|
+
var TRADE_PT_EVENT_AMOUNT_OUT_OFFSET = 138;
|
|
69872
|
+
var MERGE_EVENT_AMOUNT_SY_OUT_OFFSET = 296;
|
|
69873
|
+
async function makeRollPtTx(params) {
|
|
69874
|
+
const {
|
|
69875
|
+
program,
|
|
69876
|
+
marginfiAccount,
|
|
69877
|
+
connection,
|
|
69878
|
+
bankMap,
|
|
69879
|
+
oraclePrices,
|
|
69880
|
+
withdrawOpts,
|
|
69881
|
+
depositOpts,
|
|
69882
|
+
rollOpts,
|
|
69883
|
+
assetShareValueMultiplierByBank,
|
|
69884
|
+
addressLookupTableAccounts,
|
|
69885
|
+
crossbarUrl
|
|
69886
|
+
} = params;
|
|
69887
|
+
if (!rollOpts.maturedMarket && !rollOpts.maturedVault) {
|
|
69888
|
+
throw new Error("roll-pt: rollOpts.maturedMarket or maturedVault is required");
|
|
69889
|
+
}
|
|
69890
|
+
const merge = await resolveExponentMergeContext({
|
|
69891
|
+
connection,
|
|
69892
|
+
owner: marginfiAccount.authority,
|
|
69893
|
+
market: rollOpts.maturedMarket,
|
|
69894
|
+
vault: rollOpts.maturedVault,
|
|
69895
|
+
ptYtTokenProgram: withdrawOpts.tokenProgram,
|
|
69896
|
+
syTokenProgram: rollOpts.syTokenProgram
|
|
69897
|
+
});
|
|
69898
|
+
const clmm = await resolveExponentClmmTradePtContext({
|
|
69899
|
+
connection,
|
|
69900
|
+
owner: marginfiAccount.authority,
|
|
69901
|
+
market: rollOpts.successorMarket,
|
|
69902
|
+
ptTokenProgram: depositOpts.tokenProgram,
|
|
69903
|
+
syTokenProgram: rollOpts.syTokenProgram
|
|
69904
|
+
});
|
|
69905
|
+
const blockhash = (await connection.getLatestBlockhash("confirmed")).blockhash;
|
|
69906
|
+
const setupIxs = await makeSetupIx({
|
|
69907
|
+
connection,
|
|
69908
|
+
authority: marginfiAccount.authority,
|
|
69909
|
+
tokens: [
|
|
69910
|
+
{ mint: withdrawOpts.withdrawBank.mint, tokenProgram: withdrawOpts.tokenProgram },
|
|
69911
|
+
{ mint: merge.mergeAccounts.mintYt, tokenProgram: withdrawOpts.tokenProgram },
|
|
69912
|
+
{ mint: merge.underlying.mint, tokenProgram: merge.underlying.tokenProgram },
|
|
69913
|
+
{ mint: depositOpts.depositBank.mint, tokenProgram: depositOpts.tokenProgram }
|
|
69914
|
+
]
|
|
69915
|
+
});
|
|
69916
|
+
const { flashloanTx, swapQuote, withdrawIxs, depositIxs } = await buildRollPtFlashloanTx({
|
|
69917
|
+
params,
|
|
69918
|
+
merge,
|
|
69919
|
+
clmm,
|
|
69920
|
+
setupIxs,
|
|
69921
|
+
blockhash
|
|
69922
|
+
});
|
|
69923
|
+
const { instructions: updateFeedIxs, luts: feedLuts } = await makeSmartCrankSwbFeedIx({
|
|
69924
|
+
marginfiAccount,
|
|
69925
|
+
bankMap,
|
|
69926
|
+
oraclePrices,
|
|
69927
|
+
assetShareValueMultiplierByBank,
|
|
69928
|
+
instructions: [...withdrawIxs.instructions, ...depositIxs.instructions],
|
|
69929
|
+
program,
|
|
69930
|
+
connection,
|
|
69931
|
+
crossbarUrl
|
|
69932
|
+
});
|
|
69933
|
+
const additionalTxs = [];
|
|
69934
|
+
if (setupIxs.length > 0) {
|
|
69935
|
+
const txs = splitInstructionsToFitTransactions([], setupIxs, {
|
|
69936
|
+
blockhash,
|
|
69937
|
+
payerKey: marginfiAccount.authority,
|
|
69938
|
+
luts: addressLookupTableAccounts ?? []
|
|
69712
69939
|
});
|
|
69940
|
+
additionalTxs.push(
|
|
69941
|
+
...txs.map(
|
|
69942
|
+
(tx) => addTransactionMetadata(tx, {
|
|
69943
|
+
type: "CREATE_ATA" /* CREATE_ATA */,
|
|
69944
|
+
addressLookupTables: addressLookupTableAccounts
|
|
69945
|
+
})
|
|
69946
|
+
)
|
|
69947
|
+
);
|
|
69713
69948
|
}
|
|
69714
|
-
|
|
69715
|
-
|
|
69716
|
-
|
|
69717
|
-
|
|
69718
|
-
|
|
69719
|
-
|
|
69720
|
-
|
|
69721
|
-
|
|
69722
|
-
|
|
69723
|
-
|
|
69949
|
+
if (updateFeedIxs.length > 0) {
|
|
69950
|
+
const message = new web3_js.TransactionMessage({
|
|
69951
|
+
payerKey: marginfiAccount.authority,
|
|
69952
|
+
recentBlockhash: blockhash,
|
|
69953
|
+
instructions: updateFeedIxs
|
|
69954
|
+
}).compileToV0Message(feedLuts);
|
|
69955
|
+
additionalTxs.push(
|
|
69956
|
+
addTransactionMetadata(new web3_js.VersionedTransaction(message), {
|
|
69957
|
+
addressLookupTables: feedLuts,
|
|
69958
|
+
type: "CRANK" /* CRANK */
|
|
69959
|
+
})
|
|
69960
|
+
);
|
|
69724
69961
|
}
|
|
69725
|
-
|
|
69726
|
-
|
|
69727
|
-
|
|
69728
|
-
|
|
69729
|
-
|
|
69962
|
+
const transactions = [...additionalTxs, flashloanTx];
|
|
69963
|
+
return {
|
|
69964
|
+
transactions,
|
|
69965
|
+
actionTxIndex: transactions.length - 1,
|
|
69966
|
+
quoteResponse: swapQuote
|
|
69967
|
+
};
|
|
69730
69968
|
}
|
|
69731
|
-
function
|
|
69732
|
-
|
|
69733
|
-
|
|
69734
|
-
|
|
69735
|
-
|
|
69736
|
-
|
|
69737
|
-
|
|
69738
|
-
|
|
69739
|
-
|
|
69740
|
-
|
|
69741
|
-
|
|
69742
|
-
|
|
69743
|
-
|
|
69744
|
-
|
|
69745
|
-
|
|
69746
|
-
|
|
69969
|
+
async function buildRollPtFlashloanTx({
|
|
69970
|
+
params,
|
|
69971
|
+
merge,
|
|
69972
|
+
clmm,
|
|
69973
|
+
setupIxs,
|
|
69974
|
+
blockhash
|
|
69975
|
+
}) {
|
|
69976
|
+
const {
|
|
69977
|
+
program,
|
|
69978
|
+
marginfiAccount,
|
|
69979
|
+
bankMap,
|
|
69980
|
+
withdrawOpts,
|
|
69981
|
+
depositOpts,
|
|
69982
|
+
bankMetadataMap,
|
|
69983
|
+
connection,
|
|
69984
|
+
addressLookupTableAccounts,
|
|
69985
|
+
overrideInferAccounts,
|
|
69986
|
+
rollOpts
|
|
69987
|
+
} = params;
|
|
69988
|
+
const { withdrawBank, tokenProgram: withdrawTokenProgram, totalPositionAmount, withdrawAmount } = withdrawOpts;
|
|
69989
|
+
const { depositBank, tokenProgram: depositTokenProgram } = depositOpts;
|
|
69990
|
+
const authority = marginfiAccount.authority;
|
|
69991
|
+
if (withdrawAmount !== void 0 && withdrawAmount <= 0) {
|
|
69992
|
+
throw new Error("withdrawAmount must be greater than 0");
|
|
69747
69993
|
}
|
|
69748
|
-
const
|
|
69749
|
-
const
|
|
69750
|
-
|
|
69751
|
-
|
|
69752
|
-
|
|
69753
|
-
|
|
69994
|
+
const actualWithdrawAmount = Math.min(withdrawAmount ?? totalPositionAmount, totalPositionAmount);
|
|
69995
|
+
const isFullWithdraw = isWholePosition(
|
|
69996
|
+
{ amount: totalPositionAmount, isLending: true },
|
|
69997
|
+
actualWithdrawAmount,
|
|
69998
|
+
withdrawBank.mintDecimals
|
|
69999
|
+
);
|
|
70000
|
+
const withdrawNative = BigInt(
|
|
70001
|
+
uiToNative(actualWithdrawAmount, withdrawBank.mintDecimals).toString()
|
|
70002
|
+
);
|
|
70003
|
+
const cuRequestIxs = [
|
|
70004
|
+
web3_js.ComputeBudgetProgram.setComputeUnitLimit({ units: 12e5 }),
|
|
70005
|
+
web3_js.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1 })
|
|
70006
|
+
];
|
|
70007
|
+
const withdrawIxs = await makeWithdrawIx3({
|
|
70008
|
+
program,
|
|
70009
|
+
bank: withdrawBank,
|
|
70010
|
+
bankMap,
|
|
70011
|
+
tokenProgram: withdrawTokenProgram,
|
|
70012
|
+
amount: actualWithdrawAmount,
|
|
70013
|
+
marginfiAccount,
|
|
70014
|
+
authority,
|
|
70015
|
+
withdrawAll: isFullWithdraw,
|
|
70016
|
+
isSync: false,
|
|
70017
|
+
opts: { createAtas: false, wrapAndUnwrapSol: false, overrideInferAccounts }
|
|
69754
70018
|
});
|
|
69755
|
-
|
|
69756
|
-
|
|
69757
|
-
|
|
69758
|
-
|
|
69759
|
-
|
|
69760
|
-
|
|
70019
|
+
const mergeIx = makeExponentMergeIx(merge.mergeAccounts, withdrawNative);
|
|
70020
|
+
const depositIxs = await makeDepositIx3({
|
|
70021
|
+
program,
|
|
70022
|
+
bank: depositBank,
|
|
70023
|
+
tokenProgram: depositTokenProgram,
|
|
70024
|
+
amount: 0,
|
|
70025
|
+
accountAddress: marginfiAccount.address,
|
|
70026
|
+
authority,
|
|
70027
|
+
group: marginfiAccount.group,
|
|
70028
|
+
opts: { wrapAndUnwrapSol: false, overrideInferAccounts }
|
|
70029
|
+
});
|
|
70030
|
+
let luts;
|
|
70031
|
+
if (rollOpts.lookupTable) {
|
|
70032
|
+
const fetched = (await connection.getAddressLookupTable(rollOpts.lookupTable)).value;
|
|
70033
|
+
if (!fetched) {
|
|
70034
|
+
throw new Error(`roll-pt: PT-roll lookup table not found: ${rollOpts.lookupTable.toBase58()}`);
|
|
70035
|
+
}
|
|
70036
|
+
luts = [fetched, merge.addressLookupTable, clmm.addressLookupTable];
|
|
70037
|
+
} else {
|
|
70038
|
+
luts = [
|
|
70039
|
+
...addressLookupTableAccounts ?? [],
|
|
70040
|
+
merge.addressLookupTable,
|
|
70041
|
+
clmm.addressLookupTable
|
|
70042
|
+
];
|
|
69761
70043
|
}
|
|
69762
|
-
const
|
|
69763
|
-
|
|
69764
|
-
instructions: ixs,
|
|
70044
|
+
const redeemIxs = [...setupIxs, ...cuRequestIxs, ...withdrawIxs.instructions, mergeIx];
|
|
70045
|
+
const mergeReturn = await simulateRollReturn({
|
|
69765
70046
|
program,
|
|
69766
|
-
|
|
69767
|
-
|
|
70047
|
+
marginfiAccount,
|
|
70048
|
+
bankMap,
|
|
70049
|
+
connection,
|
|
70050
|
+
blockhash,
|
|
70051
|
+
luts,
|
|
70052
|
+
ixs: redeemIxs,
|
|
70053
|
+
programId: EXPONENT_CORE_PROGRAM_ID,
|
|
70054
|
+
label: "merge"
|
|
69768
70055
|
});
|
|
69769
|
-
|
|
69770
|
-
|
|
69771
|
-
|
|
69772
|
-
|
|
69773
|
-
|
|
69774
|
-
|
|
69775
|
-
|
|
69776
|
-
|
|
70056
|
+
const syExact = readReturnU64(mergeReturn, MERGE_EVENT_AMOUNT_SY_OUT_OFFSET, "merge amount_sy_out");
|
|
70057
|
+
if (syExact <= 0n) {
|
|
70058
|
+
throw new Error("roll-pt: merge would redeem 0 SY (empty/invalid matured vault state)");
|
|
70059
|
+
}
|
|
70060
|
+
const exactPtOut = await quoteClmmTradeOut({
|
|
70061
|
+
connection,
|
|
70062
|
+
clmm,
|
|
70063
|
+
amountInSyNative: syExact,
|
|
70064
|
+
payer: authority
|
|
70065
|
+
});
|
|
70066
|
+
const slippageBps = rollOpts.slippageBps ?? DEFAULT_ROLL_SLIPPAGE_BPS;
|
|
70067
|
+
const minPtOut = exactPtOut * BigInt(1e4 - slippageBps) / 10000n;
|
|
70068
|
+
if (minPtOut <= 0n) {
|
|
70069
|
+
throw new Error("roll-pt: quoted PT out is 0 (insufficient CLMM liquidity for this size)");
|
|
70070
|
+
}
|
|
70071
|
+
const tradeIx = makeExponentClmmTradePtIx(
|
|
70072
|
+
clmm.tradePtAccounts,
|
|
70073
|
+
exponentClmmBuyPtArgs({ amountInSyNative: syExact, minPtOutNative: minPtOut })
|
|
69777
70074
|
);
|
|
69778
|
-
|
|
69779
|
-
|
|
69780
|
-
|
|
69781
|
-
|
|
69782
|
-
|
|
69783
|
-
|
|
69784
|
-
|
|
69785
|
-
...
|
|
69786
|
-
|
|
69787
|
-
|
|
69788
|
-
|
|
69789
|
-
...c2.cranks,
|
|
69790
|
-
...c2.flashloans
|
|
69791
|
-
// secondLegFL(s)
|
|
70075
|
+
const depositIxToPatch = depositIxs.instructions.find(isDepositIx);
|
|
70076
|
+
if (!depositIxToPatch) {
|
|
70077
|
+
throw new Error("roll-pt: could not locate deposit instruction for amount patching");
|
|
70078
|
+
}
|
|
70079
|
+
patchDepositAmount(depositIxToPatch, new BN9__default.default(minPtOut.toString()));
|
|
70080
|
+
const allNonFlIxs = [
|
|
70081
|
+
...cuRequestIxs,
|
|
70082
|
+
...withdrawIxs.instructions,
|
|
70083
|
+
mergeIx,
|
|
70084
|
+
tradeIx,
|
|
70085
|
+
...depositIxs.instructions
|
|
69792
70086
|
];
|
|
69793
|
-
|
|
69794
|
-
|
|
69795
|
-
}
|
|
69796
|
-
function compoundQuoteRisk(firstLeg, secondLeg) {
|
|
69797
|
-
const compound = (a, b) => {
|
|
69798
|
-
if (a == null && b == null) return void 0;
|
|
69799
|
-
const x = Number(a ?? 0);
|
|
69800
|
-
const y = Number(b ?? 0);
|
|
69801
|
-
return String(1 - (1 - x) * (1 - y));
|
|
69802
|
-
};
|
|
69803
|
-
return {
|
|
69804
|
-
slippageBps: Math.round(
|
|
69805
|
-
(1 - (1 - firstLeg.slippageBps / 1e4) * (1 - secondLeg.slippageBps / 1e4)) * 1e4
|
|
69806
|
-
),
|
|
69807
|
-
priceImpactPct: compound(firstLeg.priceImpactPct, secondLeg.priceImpactPct)
|
|
69808
|
-
};
|
|
69809
|
-
}
|
|
69810
|
-
function mergeBridgeQuotes(firstLeg, secondLeg) {
|
|
69811
|
-
return {
|
|
69812
|
-
inAmount: firstLeg.inAmount,
|
|
69813
|
-
outAmount: secondLeg.outAmount,
|
|
69814
|
-
otherAmountThreshold: secondLeg.otherAmountThreshold,
|
|
69815
|
-
...compoundQuoteRisk(firstLeg, secondLeg),
|
|
69816
|
-
provider: firstLeg.provider
|
|
69817
|
-
};
|
|
69818
|
-
}
|
|
69819
|
-
function mergeBridgeQuotesDebt(firstLeg, secondLeg) {
|
|
69820
|
-
return {
|
|
69821
|
-
inAmount: firstLeg.outAmount,
|
|
69822
|
-
outAmount: secondLeg.inAmount,
|
|
69823
|
-
otherAmountThreshold: secondLeg.inAmount,
|
|
69824
|
-
...compoundQuoteRisk(firstLeg, secondLeg),
|
|
69825
|
-
provider: firstLeg.provider
|
|
69826
|
-
};
|
|
69827
|
-
}
|
|
69828
|
-
function mergeBridgeQuotesLoop(firstLeg, secondLeg) {
|
|
69829
|
-
return {
|
|
69830
|
-
inAmount: secondLeg.inAmount,
|
|
69831
|
-
outAmount: firstLeg.outAmount,
|
|
69832
|
-
otherAmountThreshold: firstLeg.otherAmountThreshold,
|
|
69833
|
-
...compoundQuoteRisk(firstLeg, secondLeg),
|
|
69834
|
-
provider: firstLeg.provider
|
|
69835
|
-
};
|
|
69836
|
-
}
|
|
69837
|
-
function blockhashOf(leg) {
|
|
69838
|
-
const tx = leg.transactions[0];
|
|
69839
|
-
return tx ? tx.message.recentBlockhash : web3_js.PublicKey.default.toBase58();
|
|
69840
|
-
}
|
|
69841
|
-
async function composeBridgedSwap(params) {
|
|
69842
|
-
const {
|
|
69843
|
-
firstLeg,
|
|
69844
|
-
buildSecondLeg,
|
|
70087
|
+
const { sizeConstraint } = computeFlashLoanNonSwapBudget({
|
|
70088
|
+
program,
|
|
69845
70089
|
marginfiAccount,
|
|
70090
|
+
bankMap,
|
|
70091
|
+
addressLookupTableAccounts: luts,
|
|
70092
|
+
ixs: allNonFlIxs
|
|
70093
|
+
});
|
|
70094
|
+
compileFlashloanPrecheck({
|
|
70095
|
+
allIxs: allNonFlIxs,
|
|
70096
|
+
payer: authority,
|
|
70097
|
+
luts,
|
|
70098
|
+
sizeConstraint,
|
|
70099
|
+
swapIxCount: 0,
|
|
70100
|
+
swapLutCount: 0
|
|
70101
|
+
});
|
|
70102
|
+
const flashloanTx = await makeFlashLoanTx({
|
|
69846
70103
|
program,
|
|
69847
|
-
banksMap,
|
|
69848
|
-
assetShareValueMultiplierByBank,
|
|
69849
|
-
feePayer,
|
|
69850
|
-
maxBundleTxs = MAX_BRIDGED_BUNDLE_TXS
|
|
69851
|
-
} = params;
|
|
69852
|
-
if (!firstLeg.quoteResponse) return null;
|
|
69853
|
-
const projectedAccount = projectAccountAfterFirstLeg(
|
|
69854
70104
|
marginfiAccount,
|
|
69855
|
-
|
|
70105
|
+
bankMap,
|
|
70106
|
+
addressLookupTableAccounts: luts,
|
|
70107
|
+
blockhash,
|
|
70108
|
+
ixs: allNonFlIxs,
|
|
70109
|
+
isSync: false
|
|
70110
|
+
});
|
|
70111
|
+
const txSize = getTxSize(flashloanTx);
|
|
70112
|
+
const totalKeys = getTotalAccountKeys(flashloanTx);
|
|
70113
|
+
if (txSize > MAX_TX_SIZE || totalKeys > MAX_ACCOUNT_LOCKS) {
|
|
70114
|
+
throw TransactionBuildingError.swapSizeExceededPositionSwap(txSize, totalKeys, void 0);
|
|
70115
|
+
}
|
|
70116
|
+
const swapQuote = {
|
|
70117
|
+
inAmount: syExact.toString(),
|
|
70118
|
+
outAmount: exactPtOut.toString(),
|
|
70119
|
+
otherAmountThreshold: minPtOut.toString(),
|
|
70120
|
+
slippageBps
|
|
70121
|
+
};
|
|
70122
|
+
return { flashloanTx, swapQuote, withdrawIxs, depositIxs };
|
|
70123
|
+
}
|
|
70124
|
+
async function simulateRollReturn({
|
|
70125
|
+
program,
|
|
70126
|
+
marginfiAccount,
|
|
70127
|
+
bankMap,
|
|
70128
|
+
connection,
|
|
70129
|
+
blockhash,
|
|
70130
|
+
luts,
|
|
70131
|
+
ixs,
|
|
70132
|
+
programId,
|
|
70133
|
+
label
|
|
70134
|
+
}) {
|
|
70135
|
+
const quoteTx = await makeFlashLoanTx({
|
|
69856
70136
|
program,
|
|
69857
|
-
|
|
69858
|
-
|
|
70137
|
+
marginfiAccount,
|
|
70138
|
+
bankMap,
|
|
70139
|
+
addressLookupTableAccounts: luts,
|
|
70140
|
+
blockhash,
|
|
70141
|
+
ixs,
|
|
70142
|
+
isSync: false
|
|
70143
|
+
});
|
|
70144
|
+
const sim = await connection.simulateTransaction(quoteTx, {
|
|
70145
|
+
sigVerify: false,
|
|
70146
|
+
replaceRecentBlockhash: true
|
|
70147
|
+
});
|
|
70148
|
+
const logs = sim.value.logs ?? [];
|
|
70149
|
+
if (process.env.ROLL_DEBUG) {
|
|
70150
|
+
console.error(
|
|
70151
|
+
`[roll ${label} sim] err:`,
|
|
70152
|
+
JSON.stringify(sim.value.err),
|
|
70153
|
+
"returnData:",
|
|
70154
|
+
JSON.stringify(sim.value.returnData),
|
|
70155
|
+
"logsLen:",
|
|
70156
|
+
logs.length,
|
|
70157
|
+
"truncated:",
|
|
70158
|
+
logs.some((l) => l.includes("truncated"))
|
|
70159
|
+
);
|
|
70160
|
+
}
|
|
70161
|
+
const prefix = `Program return: ${programId.toBase58()} `;
|
|
70162
|
+
const line = [...logs].reverse().find((l) => l.startsWith(prefix));
|
|
70163
|
+
if (!line) {
|
|
70164
|
+
throw new Error(
|
|
70165
|
+
`roll-pt: could not read ${label} return data from quote simulation (err=${JSON.stringify(
|
|
70166
|
+
sim.value.err
|
|
70167
|
+
)})`
|
|
70168
|
+
);
|
|
70169
|
+
}
|
|
70170
|
+
return Buffer.from(line.slice(prefix.length), "base64");
|
|
70171
|
+
}
|
|
70172
|
+
function readReturnU64(data, offset, what) {
|
|
70173
|
+
if (data.length < offset + 8) {
|
|
70174
|
+
throw new Error(`roll-pt: ${what} return data too short (${data.length} bytes)`);
|
|
70175
|
+
}
|
|
70176
|
+
return data.readBigUInt64LE(offset);
|
|
70177
|
+
}
|
|
70178
|
+
async function quoteClmmTradeOut({
|
|
70179
|
+
connection,
|
|
70180
|
+
clmm,
|
|
70181
|
+
amountInSyNative,
|
|
70182
|
+
payer
|
|
70183
|
+
}) {
|
|
70184
|
+
const excluded = /* @__PURE__ */ new Set([
|
|
70185
|
+
clmm.tradePtAccounts.tokenSyEscrow.toBase58(),
|
|
70186
|
+
clmm.tradePtAccounts.tokenFeeTreasurySy.toBase58()
|
|
70187
|
+
]);
|
|
70188
|
+
const largest = await connection.getTokenLargestAccounts(clmm.sy.mint);
|
|
70189
|
+
const funded = largest.value.find(
|
|
70190
|
+
(a) => !excluded.has(a.address.toBase58()) && BigInt(a.amount) >= amountInSyNative
|
|
69859
70191
|
);
|
|
69860
|
-
|
|
69861
|
-
|
|
69862
|
-
|
|
69863
|
-
|
|
69864
|
-
|
|
69865
|
-
|
|
69866
|
-
|
|
69867
|
-
|
|
70192
|
+
if (!funded) {
|
|
70193
|
+
throw new Error(
|
|
70194
|
+
"roll-pt: no SY holder large enough to quote the buy \u2014 the roll size exceeds available CLMM liquidity for this pair"
|
|
70195
|
+
);
|
|
70196
|
+
}
|
|
70197
|
+
const parsed = await connection.getParsedAccountInfo(funded.address);
|
|
70198
|
+
const info = parsed.value?.data?.parsed?.info;
|
|
70199
|
+
if (!info?.owner) throw new Error("roll-pt: could not resolve the quote SY holder's owner");
|
|
70200
|
+
const trader = new web3_js.PublicKey(info.owner);
|
|
70201
|
+
const ptTokenProgram = clmm.pt.tokenProgram ?? TOKEN_PROGRAM_ID;
|
|
70202
|
+
const tokenPtTrader = getAssociatedTokenAddressSync(clmm.pt.mint, trader, true, ptTokenProgram);
|
|
70203
|
+
const quoteIx = makeExponentClmmTradePtIx(
|
|
70204
|
+
{ ...clmm.tradePtAccounts, trader, tokenSyTrader: funded.address, tokenPtTrader },
|
|
70205
|
+
exponentClmmBuyPtArgs({ amountInSyNative, minPtOutNative: 1n })
|
|
69868
70206
|
);
|
|
69869
|
-
|
|
69870
|
-
|
|
70207
|
+
const createPtAta = createAssociatedTokenAccountIdempotentInstruction(
|
|
70208
|
+
payer,
|
|
70209
|
+
tokenPtTrader,
|
|
70210
|
+
trader,
|
|
70211
|
+
clmm.pt.mint,
|
|
70212
|
+
ptTokenProgram
|
|
70213
|
+
);
|
|
70214
|
+
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
70215
|
+
const message = new web3_js.TransactionMessage({
|
|
70216
|
+
payerKey: payer,
|
|
70217
|
+
recentBlockhash: blockhash,
|
|
70218
|
+
instructions: [createPtAta, quoteIx]
|
|
70219
|
+
}).compileToV0Message([clmm.addressLookupTable]);
|
|
70220
|
+
const sim = await connection.simulateTransaction(new web3_js.VersionedTransaction(message), {
|
|
70221
|
+
sigVerify: false,
|
|
70222
|
+
replaceRecentBlockhash: true
|
|
70223
|
+
});
|
|
70224
|
+
const rd = sim.value.returnData;
|
|
70225
|
+
if (process.env.ROLL_DEBUG) {
|
|
70226
|
+
console.error("[roll trade quote] err:", JSON.stringify(sim.value.err), "returnData?", !!rd);
|
|
70227
|
+
}
|
|
70228
|
+
if (!rd?.data || rd.programId !== EXPONENT_CLMM_PROGRAM_ID.toBase58()) {
|
|
70229
|
+
throw new Error(
|
|
70230
|
+
`roll-pt: CLMM trade quote produced no return data (err=${JSON.stringify(sim.value.err)})`
|
|
70231
|
+
);
|
|
70232
|
+
}
|
|
70233
|
+
const data = Buffer.from(rd.data[0], rd.data[1]);
|
|
70234
|
+
return readReturnU64(data, TRADE_PT_EVENT_AMOUNT_OUT_OFFSET, "trade_pt amount_out");
|
|
69871
70235
|
}
|
|
69872
70236
|
var MAX_BALANCES = 16;
|
|
69873
70237
|
var DEFAULT_MAX_TRANSFER_POSITIONS = 5;
|
|
@@ -71350,30 +71714,99 @@ function patchDepositAmount(ix, amountNative) {
|
|
|
71350
71714
|
}
|
|
71351
71715
|
|
|
71352
71716
|
// src/services/account/utils/bridge.utils.ts
|
|
71353
|
-
function
|
|
71354
|
-
const balance =
|
|
71717
|
+
function accountConflictsWithBridgeBank(marginfiAccount, bridgeBankPk, bridgeTokenSide) {
|
|
71718
|
+
const balance = marginfiAccount.balances.find((b) => b.active && b.bankPk.equals(bridgeBankPk));
|
|
71355
71719
|
if (!balance) return false;
|
|
71356
|
-
return
|
|
71720
|
+
return bridgeTokenSide === "deposit" ? balance.liabilityShares.gt(0) : balance.assetShares.gt(0);
|
|
71357
71721
|
}
|
|
71358
|
-
function
|
|
71359
|
-
const {
|
|
71360
|
-
const passesSideFilter =
|
|
71361
|
-
const
|
|
71362
|
-
const
|
|
71722
|
+
function resolveBridgeCandidateBanks(params) {
|
|
71723
|
+
const { prioritizedBridgeCandidateMints, groupBanks, marginfiAccount, bridgeTokenSide } = params;
|
|
71724
|
+
const passesSideFilter = bridgeTokenSide === "borrow" ? isStandardBorrowable : isStandardDepositable;
|
|
71725
|
+
const usableBridgeBanks = [];
|
|
71726
|
+
const conflictingBridgeBanks = [];
|
|
71363
71727
|
const seenMints = /* @__PURE__ */ new Set();
|
|
71364
|
-
for (const mint of
|
|
71728
|
+
for (const mint of prioritizedBridgeCandidateMints) {
|
|
71365
71729
|
const mintKey = mint.toBase58();
|
|
71366
71730
|
if (seenMints.has(mintKey)) continue;
|
|
71367
71731
|
seenMints.add(mintKey);
|
|
71368
|
-
const bank =
|
|
71732
|
+
const bank = groupBanks.find((b) => b.mint.equals(mint) && passesSideFilter(b));
|
|
71369
71733
|
if (!bank) continue;
|
|
71370
|
-
if (
|
|
71371
|
-
|
|
71734
|
+
if (accountConflictsWithBridgeBank(marginfiAccount, bank.address, bridgeTokenSide)) {
|
|
71735
|
+
conflictingBridgeBanks.push(bank);
|
|
71372
71736
|
} else {
|
|
71373
|
-
|
|
71737
|
+
usableBridgeBanks.push(bank);
|
|
71738
|
+
}
|
|
71739
|
+
}
|
|
71740
|
+
return { usableBridgeBanks, conflictingBridgeBanks };
|
|
71741
|
+
}
|
|
71742
|
+
|
|
71743
|
+
// src/services/account/utils/bridge-routing.utils.ts
|
|
71744
|
+
var DEFAULT_BRIDGE_MINTS = [USDC_MINT, WSOL_MINT, USDT_MINT];
|
|
71745
|
+
async function resolveTokenProgramForMint(mint, connection, tokenProgramCacheByMint) {
|
|
71746
|
+
const mintKey = mint.toBase58();
|
|
71747
|
+
const cached = tokenProgramCacheByMint.get(mintKey);
|
|
71748
|
+
if (cached) return cached;
|
|
71749
|
+
const owner = (await connection.getAccountInfo(mint))?.owner ?? TOKEN_PROGRAM_ID;
|
|
71750
|
+
tokenProgramCacheByMint.set(mintKey, owner);
|
|
71751
|
+
return owner;
|
|
71752
|
+
}
|
|
71753
|
+
function selectSwapBridges(args) {
|
|
71754
|
+
const prioritizedCandidateMints = (args.bridgeCandidateMints ?? DEFAULT_BRIDGE_MINTS).filter(
|
|
71755
|
+
(mint) => !mint.equals(args.sourceMint) && !mint.equals(args.destinationMint)
|
|
71756
|
+
);
|
|
71757
|
+
return resolveBridgeCandidateBanks({
|
|
71758
|
+
prioritizedBridgeCandidateMints: prioritizedCandidateMints,
|
|
71759
|
+
groupBanks: [...args.bankMap.values()],
|
|
71760
|
+
marginfiAccount: args.marginfiAccount,
|
|
71761
|
+
bridgeTokenSide: args.bridgeTokenSide
|
|
71762
|
+
});
|
|
71763
|
+
}
|
|
71764
|
+
function isAbortError(e) {
|
|
71765
|
+
return e instanceof DOMException && e.name === "AbortError";
|
|
71766
|
+
}
|
|
71767
|
+
async function tryBridgeCandidates(args) {
|
|
71768
|
+
for (const bridgeBank of args.usableBridgeBanks) {
|
|
71769
|
+
if (args.abortSignal?.aborted) {
|
|
71770
|
+
throw new DOMException("Operation was aborted", "AbortError");
|
|
71771
|
+
}
|
|
71772
|
+
try {
|
|
71773
|
+
const result = await args.buildBundleThroughBridge(bridgeBank);
|
|
71774
|
+
if (result) return result;
|
|
71775
|
+
} catch (e) {
|
|
71776
|
+
if (isAbortError(e)) throw e;
|
|
71777
|
+
console.warn(
|
|
71778
|
+
`[bridge-routing] candidate ${bridgeBank.tokenSymbol ?? bridgeBank.mint.toBase58()} failed:`,
|
|
71779
|
+
e instanceof Error ? e.message : e
|
|
71780
|
+
);
|
|
71374
71781
|
}
|
|
71375
71782
|
}
|
|
71376
|
-
|
|
71783
|
+
if (args.usableBridgeBanks.length === 0 && args.conflictingBridgeBanks.length > 0) {
|
|
71784
|
+
throw TransactionBuildingError.bridgeConflict(
|
|
71785
|
+
args.conflictingBridgeBanks.map((bank) => ({
|
|
71786
|
+
bankAddress: bank.address.toBase58(),
|
|
71787
|
+
mint: bank.mint.toBase58(),
|
|
71788
|
+
symbol: bank.tokenSymbol
|
|
71789
|
+
})),
|
|
71790
|
+
args.bridgeTokenSide
|
|
71791
|
+
);
|
|
71792
|
+
}
|
|
71793
|
+
return null;
|
|
71794
|
+
}
|
|
71795
|
+
function sharedBridgeLegContext(params) {
|
|
71796
|
+
return {
|
|
71797
|
+
program: params.program,
|
|
71798
|
+
marginfiAccount: params.marginfiAccount,
|
|
71799
|
+
connection: params.connection,
|
|
71800
|
+
bankMap: params.bankMap,
|
|
71801
|
+
oraclePrices: params.oraclePrices,
|
|
71802
|
+
bankMetadataMap: params.bankMetadataMap,
|
|
71803
|
+
assetShareValueMultiplierByBank: params.assetShareValueMultiplierByBank,
|
|
71804
|
+
swapOpts: params.swapOpts,
|
|
71805
|
+
addressLookupTableAccounts: params.addressLookupTableAccounts,
|
|
71806
|
+
overrideInferAccounts: params.overrideInferAccounts,
|
|
71807
|
+
crossbarUrl: params.crossbarUrl,
|
|
71808
|
+
swapEngineRunner: params.swapEngineRunner
|
|
71809
|
+
};
|
|
71377
71810
|
}
|
|
71378
71811
|
|
|
71379
71812
|
// src/services/price/utils/smart-crank.utils.ts
|
|
@@ -75256,6 +75689,82 @@ var MarginfiAccountWrapper = class {
|
|
|
75256
75689
|
};
|
|
75257
75690
|
return this.account.makeSwapDebtTx(fullParams);
|
|
75258
75691
|
}
|
|
75692
|
+
/**
|
|
75693
|
+
* Creates a loop (leverage) transaction with a transparent bridged (double-hop) fallback and
|
|
75694
|
+
* auto-injected client data.
|
|
75695
|
+
*
|
|
75696
|
+
* One call: tries the direct {@link makeLoopTx} first; if its borrow→deposit swap can't fit one
|
|
75697
|
+
* transaction or has no route, it loops the deposit asset against a value-equivalent borrow of a
|
|
75698
|
+
* bridge token (USDC/wSOL/USDT by default, override via `bridgeOpts.bridgeCandidateMints`) and
|
|
75699
|
+
* debt-swaps that bridge debt to the requested borrow asset — one atomic Jito bundle.
|
|
75700
|
+
* `result.bridgeMint` is set only when the bridged path was used.
|
|
75701
|
+
*
|
|
75702
|
+
* Auto-injects: program, marginfiAccount, bankMap, oraclePrices, bankMetadataMap,
|
|
75703
|
+
* addressLookupTables, assetShareValueMultiplierByBank
|
|
75704
|
+
*
|
|
75705
|
+
* @param params - Loop parameters (user provides: connection, depositOpts, borrowOpts, swapOpts, bridgeOpts?, etc.)
|
|
75706
|
+
*/
|
|
75707
|
+
async makeBridgedLoopTx(params) {
|
|
75708
|
+
return this.account.makeBridgedLoopTx({
|
|
75709
|
+
...params,
|
|
75710
|
+
program: this.client.program,
|
|
75711
|
+
bankMap: this.client.bankMap,
|
|
75712
|
+
oraclePrices: this.client.oraclePriceByBank,
|
|
75713
|
+
bankMetadataMap: this.client.bankIntegrationMap,
|
|
75714
|
+
addressLookupTableAccounts: this.client.addressLookupTables,
|
|
75715
|
+
assetShareValueMultiplierByBank: this.client.assetShareValueMultiplierByBank
|
|
75716
|
+
});
|
|
75717
|
+
}
|
|
75718
|
+
/**
|
|
75719
|
+
* Creates a collateral-swap transaction with a transparent bridged (double-hop) fallback and
|
|
75720
|
+
* auto-injected client data.
|
|
75721
|
+
*
|
|
75722
|
+
* One call: tries the direct {@link makeSwapCollateralTx} first; if the swap `A → C` can't fit
|
|
75723
|
+
* one transaction or has no route, it decomposes into `A → bridge` + `bridge → C` through a
|
|
75724
|
+
* bridge token, composed as one atomic Jito bundle. `result.bridgeMint` is set only when the
|
|
75725
|
+
* bridged path was used.
|
|
75726
|
+
*
|
|
75727
|
+
* Auto-injects: program, marginfiAccount, bankMap, oraclePrices, bankMetadataMap,
|
|
75728
|
+
* addressLookupTables, assetShareValueMultiplierByBank
|
|
75729
|
+
*
|
|
75730
|
+
* @param params - Swap collateral parameters (user provides: connection, withdrawOpts, depositOpts, swapOpts, bridgeOpts?, etc.)
|
|
75731
|
+
*/
|
|
75732
|
+
async makeBridgedSwapCollateralTx(params) {
|
|
75733
|
+
return this.account.makeBridgedSwapCollateralTx({
|
|
75734
|
+
...params,
|
|
75735
|
+
program: this.client.program,
|
|
75736
|
+
bankMap: this.client.bankMap,
|
|
75737
|
+
oraclePrices: this.client.oraclePriceByBank,
|
|
75738
|
+
bankMetadataMap: this.client.bankIntegrationMap,
|
|
75739
|
+
addressLookupTableAccounts: this.client.addressLookupTables,
|
|
75740
|
+
assetShareValueMultiplierByBank: this.client.assetShareValueMultiplierByBank
|
|
75741
|
+
});
|
|
75742
|
+
}
|
|
75743
|
+
/**
|
|
75744
|
+
* Creates a debt-swap transaction with a transparent bridged (double-hop) fallback and
|
|
75745
|
+
* auto-injected client data.
|
|
75746
|
+
*
|
|
75747
|
+
* One call: tries the direct {@link makeSwapDebtTx} first; if the swap `A → C` can't fit one
|
|
75748
|
+
* transaction or has no route, the first leg repays A by borrowing a bridge token and the second
|
|
75749
|
+
* leg repays exactly that bridge debt while borrowing C — one atomic Jito bundle.
|
|
75750
|
+
* `result.bridgeMint` is set only when the bridged path was used.
|
|
75751
|
+
*
|
|
75752
|
+
* Auto-injects: program, marginfiAccount, bankMap, oraclePrices, bankMetadataMap,
|
|
75753
|
+
* addressLookupTables, assetShareValueMultiplierByBank
|
|
75754
|
+
*
|
|
75755
|
+
* @param params - Swap debt parameters (user provides: connection, repayOpts, borrowOpts, swapOpts, bridgeOpts?, etc.)
|
|
75756
|
+
*/
|
|
75757
|
+
async makeBridgedSwapDebtTx(params) {
|
|
75758
|
+
return this.account.makeBridgedSwapDebtTx({
|
|
75759
|
+
...params,
|
|
75760
|
+
program: this.client.program,
|
|
75761
|
+
bankMap: this.client.bankMap,
|
|
75762
|
+
oraclePrices: this.client.oraclePriceByBank,
|
|
75763
|
+
bankMetadataMap: this.client.bankIntegrationMap,
|
|
75764
|
+
addressLookupTableAccounts: this.client.addressLookupTables,
|
|
75765
|
+
assetShareValueMultiplierByBank: this.client.assetShareValueMultiplierByBank
|
|
75766
|
+
});
|
|
75767
|
+
}
|
|
75259
75768
|
/**
|
|
75260
75769
|
* Creates a deposit transaction with auto-injected client data.
|
|
75261
75770
|
*
|
|
@@ -76025,6 +76534,7 @@ exports.Bank = Bank;
|
|
|
76025
76534
|
exports.BankConfig = BankConfig;
|
|
76026
76535
|
exports.BankConfigFlag = BankConfigFlag;
|
|
76027
76536
|
exports.BankVaultType = BankVaultType;
|
|
76537
|
+
exports.DEFAULT_BRIDGE_MINTS = DEFAULT_BRIDGE_MINTS;
|
|
76028
76538
|
exports.DEFAULT_CROSSBAR_URL = DEFAULT_CROSSBAR_URL;
|
|
76029
76539
|
exports.DEFAULT_FALLBACK_CROSSBAR_URL = DEFAULT_FALLBACK_CROSSBAR_URL;
|
|
76030
76540
|
exports.DEFAULT_ORACLE_MAX_AGE = DEFAULT_ORACLE_MAX_AGE;
|
|
@@ -76098,9 +76608,10 @@ exports.TransactionConfigMap = TransactionConfigMap;
|
|
|
76098
76608
|
exports.TransactionType = TransactionType;
|
|
76099
76609
|
exports.USDC_DECIMALS = USDC_DECIMALS;
|
|
76100
76610
|
exports.USDC_MINT = USDC_MINT;
|
|
76611
|
+
exports.USDT_MINT = USDT_MINT;
|
|
76101
76612
|
exports.WSOL_MINT = WSOL_MINT;
|
|
76102
76613
|
exports.ZERO_ORACLE_KEY = ZERO_ORACLE_KEY;
|
|
76103
|
-
exports.
|
|
76614
|
+
exports.accountConflictsWithBridgeBank = accountConflictsWithBridgeBank;
|
|
76104
76615
|
exports.accountFlagToBN = accountFlagToBN;
|
|
76105
76616
|
exports.addOracleToBanksIx = addOracleToBanksIx;
|
|
76106
76617
|
exports.addTransactionMetadata = addTransactionMetadata;
|
|
@@ -76305,6 +76816,7 @@ exports.hasEmodeEntryFlag = hasEmodeEntryFlag;
|
|
|
76305
76816
|
exports.hasEmodeFlag = hasEmodeFlag;
|
|
76306
76817
|
exports.hasHealthCacheFlag = hasHealthCacheFlag;
|
|
76307
76818
|
exports.healthCacheToDto = healthCacheToDto;
|
|
76819
|
+
exports.isBridgeConflictError = isBridgeConflictError;
|
|
76308
76820
|
exports.isDecomposableSwapError = isDecomposableSwapError;
|
|
76309
76821
|
exports.isDepositIx = isDepositIx;
|
|
76310
76822
|
exports.isFlashloan = isFlashloan;
|
|
@@ -76319,6 +76831,9 @@ exports.makeAddPermissionlessStakedBankIx = makeAddPermissionlessStakedBankIx;
|
|
|
76319
76831
|
exports.makeBeginFlashLoanIx = makeBeginFlashLoanIx3;
|
|
76320
76832
|
exports.makeBorrowIx = makeBorrowIx3;
|
|
76321
76833
|
exports.makeBorrowTx = makeBorrowTx;
|
|
76834
|
+
exports.makeBridgedLoopTx = makeBridgedLoopTx;
|
|
76835
|
+
exports.makeBridgedSwapCollateralTx = makeBridgedSwapCollateralTx;
|
|
76836
|
+
exports.makeBridgedSwapDebtTx = makeBridgedSwapDebtTx;
|
|
76322
76837
|
exports.makeBulkRepayTx = makeBulkRepayTx;
|
|
76323
76838
|
exports.makeBulkWithdrawTx = makeBulkWithdrawTx;
|
|
76324
76839
|
exports.makeBundleTipIx = makeBundleTipIx;
|
|
@@ -76406,10 +76921,12 @@ exports.patchDepositAmount = patchDepositAmount;
|
|
|
76406
76921
|
exports.requireBank = requireBank;
|
|
76407
76922
|
exports.requireTokenProgram = requireTokenProgram;
|
|
76408
76923
|
exports.resolveAmount = resolveAmount;
|
|
76409
|
-
exports.
|
|
76924
|
+
exports.resolveBridgeCandidateBanks = resolveBridgeCandidateBanks;
|
|
76925
|
+
exports.resolveTokenProgramForMint = resolveTokenProgramForMint;
|
|
76410
76926
|
exports.runSwapEngine = runSwapEngine;
|
|
76411
76927
|
exports.selectLutsForAccountAction = selectLutsForAccountAction;
|
|
76412
76928
|
exports.selectLutsForBanks = selectLutsForBanks;
|
|
76929
|
+
exports.selectSwapBridges = selectSwapBridges;
|
|
76413
76930
|
exports.serializeBankConfigOpt = serializeBankConfigOpt;
|
|
76414
76931
|
exports.serializeInstruction = serializeInstruction;
|
|
76415
76932
|
exports.serializeInterestRateConfig = serializeInterestRateConfig;
|
|
@@ -76420,6 +76937,7 @@ exports.serializeOracleSetupToIndex = serializeOracleSetupToIndex;
|
|
|
76420
76937
|
exports.serializeRiskTier = serializeRiskTier;
|
|
76421
76938
|
exports.serializeSwapEngineRequest = serializeSwapEngineRequest;
|
|
76422
76939
|
exports.serializeSwapEngineResult = serializeSwapEngineResult;
|
|
76940
|
+
exports.sharedBridgeLegContext = sharedBridgeLegContext;
|
|
76423
76941
|
exports.shortenAddress = shortenAddress;
|
|
76424
76942
|
exports.simulateAccountHealthCache = simulateAccountHealthCache;
|
|
76425
76943
|
exports.simulateAccountHealthCacheWithFallback = simulateAccountHealthCacheWithFallback;
|
|
@@ -76435,6 +76953,7 @@ exports.toEmodeSettingsDto = toEmodeSettingsDto;
|
|
|
76435
76953
|
exports.toInterestRateConfigDto = toInterestRateConfigDto;
|
|
76436
76954
|
exports.toJupiterConfig = toJupiterConfig;
|
|
76437
76955
|
exports.toNumber = toNumber;
|
|
76956
|
+
exports.tryBridgeCandidates = tryBridgeCandidates;
|
|
76438
76957
|
exports.uiToNative = uiToNative;
|
|
76439
76958
|
exports.uiToNativeBigNumber = uiToNativeBigNumber;
|
|
76440
76959
|
exports.validatorStakeGroupToDto = validatorStakeGroupToDto;
|