diff --git a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx index 215c35d40b..2eafdaa0a0 100644 --- a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx +++ b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx @@ -5,7 +5,7 @@ import Image from "next/image"; import { IconMinus, IconX, IconPlus, IconLoader2 } from "@tabler/icons-react"; import { Transaction, VersionedTransaction } from "@solana/web3.js"; -import { MultiStepToastHandle, cn, extractErrorString, capture, fetchPriorityFee } from "@mrgnlabs/mrgn-utils"; +import { MultiStepToastHandle, cn, extractErrorString, capture, ClosePositionActionTxns } from "@mrgnlabs/mrgn-utils"; import { ActiveBankInfo, ActionType } from "@mrgnlabs/marginfi-v2-ui-state"; import { useConnection } from "~/hooks/use-connection"; @@ -41,11 +41,8 @@ export const PositionActionButtons = ({ const { connection } = useConnection(); const { wallet, connected } = useWallet(); const [platformFeeBps] = useUiStore((state) => [state.platformFeeBps]); - const [actionTransaction, setActionTransaction] = React.useState<{ - closeTxn: VersionedTransaction | Transaction; - feedCrankTxs: VersionedTransaction[]; - quote?: QuoteResponse; - } | null>(null); + const [actionTransaction, setActionTransaction] = React.useState(null); + const [isLoading, setIsLoading] = React.useState(false); const [multiStepToast, setMultiStepToast] = React.useState(null); const [isClosing, setIsClosing] = React.useState(false); @@ -56,16 +53,13 @@ export const PositionActionButtons = ({ state.setIsRefreshingStore, state.nativeSolBalance, ]); - const [slippageBps, priorityType, broadcastType, maxCap, maxCapType, setIsActionComplete, setPreviousTxn] = - useUiStore((state) => [ - state.slippageBps, - state.priorityType, - state.broadcastType, - state.maxCap, - state.maxCapType, - state.setIsActionComplete, - state.setPreviousTxn, - ]); + const [slippageBps, priorityFees, broadcastType, setIsActionComplete, setPreviousTxn] = useUiStore((state) => [ + state.slippageBps, + state.priorityFees, + state.broadcastType, + state.setIsActionComplete, + state.setPreviousTxn, + ]); const depositBanks = React.useMemo(() => { const tokenBank = activeGroup.pool.token.isActive ? activeGroup.pool.token : null; @@ -107,21 +101,18 @@ export const PositionActionButtons = ({ throw new Error("Invalid client"); } - const priorityFees = await fetchPriorityFee(maxCapType, maxCap, broadcastType, priorityType, connection); - const txns = await calculateClosePositions({ marginfiAccount: activeGroup.selectedAccount, depositBanks: depositBanks, borrowBank: borrowBank, slippageBps, connection: connection, - priorityFees, platformFeeBps, }); if ("description" in txns) { throw new Error(txns?.description ?? "Something went wrong."); - } else if ("closeTxn" in txns) { + } else if ("actionTxn" in txns) { setActionTransaction(txns); } } catch (error: any) { @@ -134,38 +125,19 @@ export const PositionActionButtons = ({ setMultiStepToast(multiStepToast); } setIsClosing(false); - }, [ - activeGroup, - borrowBank, - depositBanks, - maxCapType, - maxCap, - broadcastType, - priorityType, - connection, - slippageBps, - platformFeeBps, - ]); + }, [activeGroup, borrowBank, depositBanks, connection, slippageBps, platformFeeBps]); const processTransaction = React.useCallback(async () => { try { setIsLoading(true); let txnSig: string | string[] = ""; - if (!actionTransaction || !multiStepToast) throw new Error("Action not ready"); - if (actionTransaction.closeTxn instanceof Transaction) { - txnSig = await activeGroup.client.processTransaction(actionTransaction.closeTxn, { - broadcastType: broadcastType, - }); - multiStepToast.setSuccessAndNext(); - } else { - const priorityFees = await fetchPriorityFee(maxCapType, maxCap, broadcastType, priorityType, connection); - txnSig = await activeGroup.client.processTransactions( - [...actionTransaction.feedCrankTxs, actionTransaction.closeTxn], - { broadcastType: broadcastType, ...priorityFees } - ); - multiStepToast.setSuccessAndNext(); - } + if (!actionTransaction?.actionTxn || !multiStepToast) throw new Error("Action not ready"); + txnSig = await activeGroup.client.processTransactions( + [...actionTransaction.additionalTxns, actionTransaction.actionTxn], + { broadcastType: broadcastType, ...priorityFees } + ); + multiStepToast.setSuccessAndNext(); if (txnSig) { setActionTransaction(null); @@ -220,14 +192,12 @@ export const PositionActionButtons = ({ activeGroup.pool.quoteTokens, activeGroup?.groupPk, broadcastType, - maxCapType, - maxCap, - priorityType, - connection, + priorityFees, setIsActionComplete, setPreviousTxn, setIsRefreshingStore, refreshGroup, + connection, wallet, ]); @@ -431,40 +401,43 @@ export const PositionActionButtons = ({ )} - {actionTransaction?.quote?.priceImpactPct && ( + {actionTransaction?.actionQuote?.priceImpactPct && ( <>
Price impact
0.05 + Number(actionTransaction.actionQuote.priceImpactPct) > 0.05 ? "text-mrgn-error" - : Number(actionTransaction.quote.priceImpactPct) > 0.01 + : Number(actionTransaction.actionQuote.priceImpactPct) > 0.01 ? "text-alert-foreground" : "text-mrgn-success", "text-right" )} > - {percentFormatter.format(Number(actionTransaction.quote.priceImpactPct))} + {percentFormatter.format(Number(actionTransaction.actionQuote.priceImpactPct))}
)} - {actionTransaction?.quote?.slippageBps && ( + {actionTransaction?.actionQuote?.slippageBps && ( <>
Slippage
500 && "text-alert-foreground", "text-right")} + className={cn( + actionTransaction.actionQuote.slippageBps > 500 && "text-alert-foreground", + "text-right" + )} > - {percentFormatter.format(Number(actionTransaction.quote.slippageBps) / 10000)} + {percentFormatter.format(Number(actionTransaction.actionQuote.slippageBps) / 10000)}
)}
Platform fee
- {actionTransaction?.quote?.platformFee?.feeBps && ( + {actionTransaction?.actionQuote?.platformFee?.feeBps && ( <>
- {percentFormatter.format(actionTransaction.quote.platformFee.feeBps / 10000)} + {percentFormatter.format(actionTransaction.actionQuote.platformFee.feeBps / 10000)}
)} diff --git a/apps/marginfi-v2-trading/src/pages/_app.tsx b/apps/marginfi-v2-trading/src/pages/_app.tsx index fd1857c06a..3cf78dff26 100644 --- a/apps/marginfi-v2-trading/src/pages/_app.tsx +++ b/apps/marginfi-v2-trading/src/pages/_app.tsx @@ -12,7 +12,7 @@ import { ToastContainer } from "react-toastify"; import { Analytics } from "@vercel/analytics/react"; import { BankMetadataRaw } from "@mrgnlabs/mrgn-common"; import { DEFAULT_MAX_CAP, Desktop, Mobile, init as initAnalytics } from "@mrgnlabs/mrgn-utils"; -import { ActionProvider, AuthDialog } from "@mrgnlabs/mrgn-ui"; +import { ActionProvider } from "~/components/action-box-v2"; import { generateEndpoint } from "~/rpc.utils"; import config from "~/config"; @@ -30,6 +30,7 @@ import { Header } from "~/components/common/Header"; import { Footer } from "~/components/desktop/Footer"; import "react-toastify/dist/ReactToastify.min.css"; +import { AuthDialog } from "~/components/wallet-v2"; require("~/styles/globals.css"); require("~/styles/fonts.css"); diff --git a/apps/marginfi-v2-trading/src/pages/api/priorityFees.ts b/apps/marginfi-v2-trading/src/pages/api/priorityFees.ts new file mode 100644 index 0000000000..fdfb777dfe --- /dev/null +++ b/apps/marginfi-v2-trading/src/pages/api/priorityFees.ts @@ -0,0 +1,44 @@ +import { getCalculatedPrioritizationFeeByPercentile } from "@mrgnlabs/mrgn-common"; +import { Connection } from "@solana/web3.js"; +import { NextApiRequest, NextApiResponse } from "next"; + +const enum PriotitizationFeeLevels { + LOW = 2500, + MEDIAN = 5000, + HIGH = 7500, + MAX = 10000, +} +/* + Get jito tip data for at least 50 percentile result +*/ +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + // use abort controller to restrict fetch to 5 seconds + const controller = new AbortController(); + const timeoutId = setTimeout(() => { + controller.abort(); + }, 5000); + + const connection = new Connection(process.env.PRIVATE_RPC_ENDPOINT_OVERRIDE || ""); + + // Fetch from API and update cache + try { + const data = await getCalculatedPrioritizationFeeByPercentile( + connection, + { + lockedWritableAccounts: [], // TODO: investigate this + percentile: PriotitizationFeeLevels.HIGH, + fallback: false, + }, + 20 + ); + + clearTimeout(timeoutId); + + // cache for 4 minutes + res.setHeader("Cache-Control", "s-maxage=30, stale-while-revalidate=59"); + res.status(200).json(data); + } catch (error) { + console.error("Error:", error); + res.status(500).json({ error: "Error fetching data" }); + } +} diff --git a/apps/marginfi-v2-trading/src/utils/tradingActions.ts b/apps/marginfi-v2-trading/src/utils/tradingActions.ts index 71f4ce966e..cfe20bf255 100644 --- a/apps/marginfi-v2-trading/src/utils/tradingActions.ts +++ b/apps/marginfi-v2-trading/src/utils/tradingActions.ts @@ -31,6 +31,7 @@ import { STATIC_SIMULATION_ERRORS, extractErrorString, LoopActionTxns, + ClosePositionActionTxns, } from "@mrgnlabs/mrgn-utils"; import { ExtendedBankInfo, clearAccountCache, ActiveBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; @@ -301,7 +302,6 @@ export async function calculateClosePositions({ depositBanks, slippageBps, connection, - priorityFees, platformFeeBps, }: { marginfiAccount: MarginfiAccountWrapper; @@ -309,25 +309,16 @@ export async function calculateClosePositions({ depositBanks: ActiveBankInfo[]; slippageBps: number; connection: Connection; - priorityFees: PriorityFees; - platformFeeBps?: number; -}): Promise< - | { - closeTxn: VersionedTransaction | Transaction; - feedCrankTxs: VersionedTransaction[]; - quote?: QuoteResponse; - } - | ActionMessageType -> { + platformFeeBps: number; +}): Promise { // user is borrowing and depositing if (borrowBank && depositBanks.length === 1) { return calculateBorrowLendPositionParams({ marginfiAccount, borrowBank, depositBank: depositBanks[0], - slippageBps, connection, - priorityFees, + slippageBps, platformFeeBps, }); } @@ -341,8 +332,9 @@ export async function calculateClosePositions({ })) ); return { - closeTxn: txn, - feedCrankTxs: [], + actionTxn: txn, + additionalTxns: [], + actionQuote: null, }; } diff --git a/packages/marginfi-client-v2/src/models/account/wrapper.ts b/packages/marginfi-client-v2/src/models/account/wrapper.ts index f223bc99f9..c6388bc1e7 100644 --- a/packages/marginfi-client-v2/src/models/account/wrapper.ts +++ b/packages/marginfi-client-v2/src/models/account/wrapper.ts @@ -447,7 +447,10 @@ class MarginfiAccountWrapper { // creates atas if needed const setupIxs = await this.makeSetupIx([borrowBankAddress, depositBankAddress]); - const cuRequestIxs = this.makeComputeBudgetIx(); + const cuRequestIxs = + this.makeComputeBudgetIx().length > 0 + ? this.makeComputeBudgetIx() + : [ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 })]; // tiny priority fee just in case bundle fails const [priorityFeeIx] = makePriorityFeeIx(0.00001); const withdrawIxs = await this.makeWithdrawIx( @@ -587,7 +590,10 @@ class MarginfiAccountWrapper { // creates atas if needed const setupIxs = await this.makeSetupIx([borrowBankAddress, depositBankAddress]); - const cuRequestIxs = this.makeComputeBudgetIx(); + const cuRequestIxs = + this.makeComputeBudgetIx().length > 0 + ? this.makeComputeBudgetIx() + : [ComputeBudgetProgram.setComputeUnitLimit({ units: 500_000 })]; // tiny priority fee just in case bundle fails const [priorityFeeIx] = makePriorityFeeIx(0.00001); const borrowIxs = await this.makeBorrowIx( diff --git a/packages/marginfi-client-v2/src/services/transaction/transaction.helper.ts b/packages/marginfi-client-v2/src/services/transaction/transaction.helper.ts index 9a4658bde8..fba4306a7c 100644 --- a/packages/marginfi-client-v2/src/services/transaction/transaction.helper.ts +++ b/packages/marginfi-client-v2/src/services/transaction/transaction.helper.ts @@ -105,6 +105,7 @@ export function formatTransactions( transactions.forEach((tx, idx) => { const cu = tx.unitsConsumed ? Math.min(tx.unitsConsumed + 50_000, 1_400_000) : getComputeBudgetUnits(tx); + const priorityFeeUi = microLamportsToUi(priorityFeeMicro, cu); let updatedFees = priorityFeeMicro; diff --git a/packages/mrgn-common/src/modules/transactions/transaction.utils.ts b/packages/mrgn-common/src/modules/transactions/transaction.utils.ts index f20aae67bc..8d94d8a00d 100644 --- a/packages/mrgn-common/src/modules/transactions/transaction.utils.ts +++ b/packages/mrgn-common/src/modules/transactions/transaction.utils.ts @@ -204,7 +204,6 @@ export function replaceV0TxInstructions( const updatedCuLimitIx = additionalIxs.find( (a) => decodeComputeBudgetInstruction(a).instructionType === "SetComputeUnitLimit" ); - // replace priority fee instruction if (decoded.instructionType === "SetComputeUnitPrice" && updatedCuPriceIx) { //subtract the additional instruction from the additional instructions array diff --git a/packages/mrgn-ui/src/components/action-box-v2/contexts/action/action.context.tsx b/packages/mrgn-ui/src/components/action-box-v2/contexts/action/action.context.tsx index 5c7bba2388..977b62ab3b 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/contexts/action/action.context.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/contexts/action/action.context.tsx @@ -16,5 +16,9 @@ export const ActionProvider: React.FC { const context = React.useContext(ActionContext); + + if (!context) { + console.warn("useActionContext called outside provider or with null context"); + } return context; }; diff --git a/packages/mrgn-utils/src/actions/flashloans/builders.ts b/packages/mrgn-utils/src/actions/flashloans/builders.ts index 45f7cf78aa..ac35a0016d 100644 --- a/packages/mrgn-utils/src/actions/flashloans/builders.ts +++ b/packages/mrgn-utils/src/actions/flashloans/builders.ts @@ -16,8 +16,11 @@ import { deserializeInstruction, getAdressLookupTableAccounts, getSwapQuoteWithR import { isWholePosition } from "../../mrgnUtils"; import { ActionMessageType, + CalculateClosePositionProps, CalculateLoopingProps, CalculateRepayCollateralProps, + ClosePositionActionTxns, + ClosePositionProps, LoopActionTxns, LoopingObject, LoopingProps, @@ -99,7 +102,7 @@ export async function calculateRepayCollateralParams({ quote: swapQuote, repayAmount: amountToRepay, }); - console.log("txn", txn); + if (txn.flashloanTx) { return { repayCollatObject: { @@ -128,44 +131,29 @@ export async function calculateRepayCollateralParams({ * Calculates the parameters for a close all positions flashloan */ export async function calculateBorrowLendPositionParams({ - marginfiAccount, - borrowBank, - depositBank, slippageBps, - connection, - priorityFees, platformFeeBps, -}: { - marginfiAccount: MarginfiAccountWrapper; - borrowBank: ActiveBankInfo; - depositBank: ActiveBankInfo; - slippageBps: number; - connection: Connection; - priorityFees: PriorityFees; - platformFeeBps?: number; -}): Promise< - | { - closeTxn: VersionedTransaction; - feedCrankTxs: VersionedTransaction[]; - quote: QuoteResponse; - } - | ActionMessageType -> { + ...closePostionProps +}: CalculateClosePositionProps): Promise { let firstQuote; const maxAccountsArr = [undefined, 50, 40, 30]; - if (!borrowBank.isActive) throw new Error("not active"); + if (!closePostionProps.borrowBank.isActive) throw new Error("not active"); - const maxAmount = await calculateMaxRepayableCollateral(borrowBank, depositBank, slippageBps); + const maxAmount = await calculateMaxRepayableCollateral( + closePostionProps.borrowBank, + closePostionProps.depositBank, + slippageBps + ); if (!maxAmount) return STATIC_SIMULATION_ERRORS.CLOSE_POSITIONS_FL_FAILED; for (const maxAccounts of maxAccountsArr) { const isTxnSplit = maxAccounts === 30; const quoteParams = { - amount: uiToNative(maxAmount, depositBank.info.state.mintDecimals).toNumber(), - inputMint: depositBank.info.state.mint.toBase58(), - outputMint: borrowBank.info.state.mint.toBase58(), + amount: uiToNative(maxAmount, closePostionProps.depositBank.info.state.mintDecimals).toNumber(), + inputMint: closePostionProps.depositBank.info.state.mint.toBase58(), + outputMint: closePostionProps.borrowBank.info.state.mint.toBase58(), slippageBps: slippageBps, platformFeeBps: platformFeeBps, maxAccounts: maxAccounts, @@ -179,18 +167,17 @@ export async function calculateBorrowLendPositionParams({ } if (swapQuote) { - const txn = await verifyTxSizeCloseBorrowLendPosition( - marginfiAccount, - depositBank, - borrowBank, - swapQuote, - connection, - isTxnSplit, - priorityFees - ); + const txn = await verifyTxSizeCloseBorrowLendPosition({ + ...closePostionProps, + quote: swapQuote, + }); if (txn.flashloanTx) { - return { closeTxn: txn.flashloanTx, feedCrankTxs: txn.feedCrankTxs, quote: swapQuote }; + return { + actionTxn: txn.flashloanTx, + additionalTxns: txn.additionalTxs, + actionQuote: swapQuote, + }; } else if (txn.error && maxAccounts === maxAccountsArr[maxAccountsArr.length - 1]) { return txn.error; } @@ -475,17 +462,7 @@ export async function closePositionBuilder({ borrowBank, quote, connection, - isTxnSplit, - priorityFees, -}: { - marginfiAccount: MarginfiAccountWrapper; - depositBank: ActiveBankInfo; - borrowBank: ActiveBankInfo; - quote: QuoteResponse; - connection: Connection; - isTxnSplit?: boolean; - priorityFees: PriorityFees; -}) { +}: ClosePositionProps) { const jupiterQuoteApi = createJupiterApiClient(); let feeAccountInfo: AccountInfo | null = null; @@ -502,28 +479,23 @@ export async function closePositionBuilder({ feeAccount: feeAccountInfo ? feeAccount : undefined, }, }); - - // const setupIxs = setupInstructions.length > 0 ? setupInstructions.map(deserializeInstruction) : []; //**not optional but man0s smart** const swapIx = deserializeInstruction(swapInstruction); - // const swapcleanupIx = cleanupInstruction ? [deserializeInstruction(cleanupInstruction)] : []; **optional** - // tokenLedgerInstruction **also optional** const swapLUTs: AddressLookupTableAccount[] = []; swapLUTs.push(...(await getAdressLookupTableAccounts(connection, addressLookupTableAddresses))); - // TODO: refactor - const { flashloanTx, feedCrankTxs, addressLookupTableAccounts } = await marginfiAccount.makeRepayWithCollatTx( - borrowBank.position.amount, - depositBank.position.amount, - borrowBank.address, - depositBank.address, - true, - true, - [swapIx], - swapLUTs, - priorityFees.bundleTipUi ?? priorityFees.priorityFeeMicro, - isTxnSplit - ); + const { flashloanTx, additionalTxs, txOverflown } = await marginfiAccount.makeRepayWithCollatTxV2({ + repayAmount: borrowBank.position.amount, + withdrawAmount: depositBank.position.amount, + borrowBankAddress: borrowBank.address, + depositBankAddress: depositBank.address, + withdrawAll: true, + repayAll: true, + swap: { + instructions: [swapIx], + lookupTables: swapLUTs, + }, + }); - return { flashloanTx, feedCrankTxs, addressLookupTableAccounts }; + return { flashloanTx, additionalTxs, txOverflown }; } diff --git a/packages/mrgn-utils/src/actions/flashloans/helpers.ts b/packages/mrgn-utils/src/actions/flashloans/helpers.ts index 34972e7b9d..ee2f27b620 100644 --- a/packages/mrgn-utils/src/actions/flashloans/helpers.ts +++ b/packages/mrgn-utils/src/actions/flashloans/helpers.ts @@ -12,7 +12,7 @@ import { ActiveBankInfo, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state import { ExtendedV0Transaction, nativeToUi, TransactionBroadcastType, uiToNative } from "@mrgnlabs/mrgn-common"; import { STATIC_SIMULATION_ERRORS } from "../../errors"; -import { ActionMessageType, LoopingProps, RepayWithCollatProps } from "../types"; +import { ActionMessageType, ClosePositionProps, LoopingProps, RepayWithCollatProps } from "../types"; import { closePositionBuilder, loopingBuilder, repayWithCollatBuilder } from "./builders"; import { getSwapQuoteWithRetry } from "../helpers"; @@ -57,49 +57,36 @@ export async function verifyTxSizeLooping(props: LoopingProps): Promise { + props: ClosePositionProps +): Promise { try { - if (quoteResponse.slippageBps > 150) { + if (props.quote.slippageBps > 150) { throw Error("Slippage too high"); } - if (Number(quoteResponse.priceImpactPct) > 0.05) { + if (Number(props.quote.priceImpactPct) > 0.05) { throw Error("Price impact too high"); } - const builder = await closePositionBuilder({ - marginfiAccount, - depositBank, - borrowBank, - quote: quoteResponse, - connection, - isTxnSplit, - priorityFees, - }); + const builder = await closePositionBuilder(props); - const txCheck = verifyFlashloanTxSize(builder); - if (!txCheck) throw Error("this should not happen"); - - return txCheck; + if (builder.txOverflown) { + return { + flashloanTx: null, + additionalTxs: [], + error: STATIC_SIMULATION_ERRORS.TX_SIZE, + }; + } else { + return { + ...builder, + }; + } } catch (error) { console.error(error); return { flashloanTx: null, - feedCrankTxs: [], - addressLookupTableAccounts: [], - error: STATIC_SIMULATION_ERRORS.CLOSE_POSITIONS_FL_FAILED, + additionalTxs: [], + error: STATIC_SIMULATION_ERRORS.TX_SIZE, }; } } @@ -125,7 +112,6 @@ export async function verifyTxSizeCollat(props: RepayWithCollatProps): Promise { + slippageBps: number; + platformFeeBps: number; +} + +export type ClosePositionProps = { + marginfiAccount: MarginfiAccountWrapper; + depositBank: ActiveBankInfo; + borrowBank: ActiveBankInfo; + quote: QuoteResponse; + connection: Connection; +}; + export type RepayWithCollatProps = { marginfiAccount: MarginfiAccountWrapper; repayAmount: number; diff --git a/packages/mrgn-utils/src/priority.utils.ts b/packages/mrgn-utils/src/priority.utils.ts index a029e9b4f5..65341be0ea 100644 --- a/packages/mrgn-utils/src/priority.utils.ts +++ b/packages/mrgn-utils/src/priority.utils.ts @@ -168,7 +168,7 @@ type PriorityFeesPercentile = { }; export const getRpcPriorityFeeMicroLamports = async (connection: Connection, priorityType: TransactionPriorityType) => { - const MIN_PRIORITY_FEE = 0.00001; + const MIN_PRIORITY_FEE = 50_000; const response = await fetch("/api/priorityFees", { method: "GET", @@ -192,7 +192,6 @@ export const getRpcPriorityFeeMicroLamports = async (connection: Connection, pri if (!response || !response.ok) { console.error("Failed to fetch priority fees"); - return MIN_PRIORITY_FEE; } else { priorityFees = await response.json(); }