diff --git a/src/bots/makerBidAskTwapCrank.ts b/src/bots/makerBidAskTwapCrank.ts index f6d0058e..aceab428 100644 --- a/src/bots/makerBidAskTwapCrank.ts +++ b/src/bots/makerBidAskTwapCrank.ts @@ -57,7 +57,7 @@ const TX_LAND_RATE_THRESHOLD = process.env.TX_LAND_RATE_THRESHOLD ? parseFloat(process.env.TX_LAND_RATE_THRESHOLD) || 0.5 : 0.5; const NUM_MAKERS_TO_LOOK_AT_FOR_TWAP_CRANK = 2; -const TX_PER_JITO_BUNDLE = 5; +const TX_PER_JITO_BUNDLE = 3; function isCriticalError(e: Error): boolean { // retrying on this error is standard diff --git a/src/bundleSender.ts b/src/bundleSender.ts index 3126c47e..3156abef 100644 --- a/src/bundleSender.ts +++ b/src/bundleSender.ts @@ -503,10 +503,7 @@ export class BundleSender { } // +1 for tip tx, jito max is 5 - let b: Bundle | Error = new Bundle( - signedTxs, - Math.max(signedTxs.length + 1, 5) - ); + let b: Bundle | Error = new Bundle(signedTxs, maxAllowedTxs); if (addTipTx) { const tipAccountToUse = diff --git a/src/experimental-bots/filler/fillerMultithreaded.ts b/src/experimental-bots/filler/fillerMultithreaded.ts index 2b68f79e..710ac3aa 100644 --- a/src/experimental-bots/filler/fillerMultithreaded.ts +++ b/src/experimental-bots/filler/fillerMultithreaded.ts @@ -61,6 +61,7 @@ import { // getStaleOracleMarketIndexes, handleSimResultError, logMessageForNodeToFill, + MEMO_PROGRAM_ID, removePythIxs, simulateAndGetTxWithCUs, SimulateAndGetTxWithCUsResponse, @@ -1236,6 +1237,8 @@ export class FillerMultithreaded { ); } + const nonActionIxCount = ixs.length; + nodeToTrigger.node.haveTrigger = true; // @ts-ignore const buffer = Buffer.from(nodeToTrigger.node.userAccountData.data); @@ -1288,6 +1291,15 @@ export class FillerMultithreaded { ixs = removePythIxs(ixs); } + if (ixs.length === nonActionIxCount) { + logger.warn( + `${logPrefix} No ixs in trigger tx (account: ${ + nodeToTrigger.node.userAccount + }, order ${nodeToTrigger.node.order.orderId.toString()})` + ); + return; + } + const simResult = await simulateAndGetTxWithCUs({ ixs, connection: this.driftClient.connection, @@ -1572,7 +1584,9 @@ export class FillerMultithreaded { ixs.push(...pythIxs); } - if (!buildForBundle) { + if (buildForBundle) { + ixs.push(this.bundleSender!.getTipIx()); + } else { ixs.push( getPriorityFeeInstruction( Math.floor( @@ -1607,7 +1621,10 @@ export class FillerMultithreaded { let makerInfosToUse = makerInfos; const buildTxWithMakerInfos = async ( makers: DataAndSlot[] - ): Promise => { + ): Promise => { + if (makers.length === 0) { + return undefined; + } ixs.push( await this.driftClient.getFillPerpOrderIx( await getUserAccountPublicKey( @@ -1664,6 +1681,13 @@ export class FillerMultithreaded { ixs = removePythIxs(ixs); } + ixs.push( + new TransactionInstruction({ + programId: MEMO_PROGRAM_ID, + keys: [], + data: Buffer.from(`mm-perp ${fillTxId} ${makers.length}`, 'utf-8'), + }) + ); const simResult = await simulateAndGetTxWithCUs({ ixs, connection: this.driftClient.connection, @@ -1694,6 +1718,9 @@ export class FillerMultithreaded { }; let simResult = await buildTxWithMakerInfos(makerInfosToUse); + if (simResult === undefined) { + return true; + } let txAccounts = simResult.tx.message.getAccountKeys({ addressLookupTableAccounts: this.lookupTableAccounts, }).length; @@ -1706,9 +1733,6 @@ export class FillerMultithreaded { ); makerInfosToUse = makerInfosToUse.slice(0, makerInfosToUse.length - 1); simResult = await buildTxWithMakerInfos(makerInfosToUse); - txAccounts = simResult.tx.message.getAccountKeys({ - addressLookupTableAccounts: this.lookupTableAccounts!, - }).length; } if (makerInfosToUse.length === 0) { @@ -1718,14 +1742,27 @@ export class FillerMultithreaded { return true; } + if (simResult === undefined) { + logger.error( + `${logPrefix} No simResult after ${attempt} attempts (fillTxId: ${fillTxId})` + ); + return true; + } + + txAccounts = simResult.tx.message.getAccountKeys({ + addressLookupTableAccounts: this.lookupTableAccounts!, + }).length; + logger.info( - `${logPrefix} tryFillMultiMakerPerpNodes estimated CUs: ${simResult.cuEstimate} (fillTxId: ${fillTxId})` + `${logPrefix} tryFillMultiMakerPerpNodes estimated CUs: ${ + simResult!.cuEstimate + } (fillTxId: ${fillTxId})` ); - if (simResult.simError) { + if (simResult!.simError) { logger.error( `${logPrefix} Error simulating multi maker perp node (fillTxId: ${fillTxId}): ${JSON.stringify( - simResult.simError + simResult!.simError )}\nTaker slot: ${takerUserSlot}\nMaker slots: ${makerInfosToUse .map((m) => ` ${m.data.maker.toBase58()}: ${m.slot}`) .join('\n')}` @@ -1735,7 +1772,7 @@ export class FillerMultithreaded { this.sendFillTxAndParseLogs( fillTxId, [nodeToFill], - simResult.tx, + simResult!.tx, buildForBundle ); } else { @@ -1782,7 +1819,9 @@ export class FillerMultithreaded { ixs.push(...pythIxs); } - if (!buildForBundle) { + if (buildForBundle) { + ixs.push(this.bundleSender!.getTipIx()); + } else { ixs.push( getPriorityFeeInstruction( Math.floor( @@ -1862,6 +1901,14 @@ export class FillerMultithreaded { ixs = removePythIxs(ixs); } + ixs.push( + new TransactionInstruction({ + programId: MEMO_PROGRAM_ID, + keys: [], + data: Buffer.from(`ff-perp ${fillTxId}`, 'utf-8'), + }) + ); + const simResult = await simulateAndGetTxWithCUs({ ixs, connection: this.driftClient.connection, @@ -2056,6 +2103,8 @@ export class FillerMultithreaded { ); } + const nonActionIxCount = ixs.length; + ixs.push( ...(await this.driftClient.getSettlePNLsIxs( [ @@ -2070,6 +2119,11 @@ export class FillerMultithreaded { )) ); + if (ixs.length === nonActionIxCount) { + logger.warn(`${logPrefix} No ixs in settlePnls tx`); + return; + } + const simResult = await simulateAndGetTxWithCUs({ ixs, connection: this.driftClient.connection, diff --git a/src/experimental-bots/spotFiller/spotFillerMultithreaded.ts b/src/experimental-bots/spotFiller/spotFillerMultithreaded.ts index 4286d469..ad5a159f 100644 --- a/src/experimental-bots/spotFiller/spotFillerMultithreaded.ts +++ b/src/experimental-bots/spotFiller/spotFillerMultithreaded.ts @@ -48,6 +48,7 @@ import { RuntimeSpec, } from '../../metrics'; import { + MEMO_PROGRAM_ID, SimulateAndGetTxWithCUsResponse, getAllPythOracleUpdateIxs, getFillSignatureFromUserAccountAndOrderId, @@ -721,6 +722,8 @@ export class SpotFillerMultithreaded { ); } + const nonActionIxCount = ixs.length; + ixs.push( await this.driftClient.getTriggerOrderIx( new PublicKey(nodeToTrigger.node.userAccount), @@ -736,6 +739,13 @@ export class SpotFillerMultithreaded { ); } + if (ixs.length === nonActionIxCount) { + logger.warn( + `${logPrefix} No ixs in trigger tx for (account: ${nodeToTrigger.node.userAccount.toString()}, order: ${nodeToTrigger.node.order.orderId.toString()})` + ); + return; + } + const simResult = await simulateAndGetTxWithCUs({ ixs, connection: this.driftClient.connection, @@ -993,7 +1003,10 @@ export class SpotFillerMultithreaded { let makerInfosToUse = makerInfos; const buildTxWithMakerInfos = async ( makers: DataAndSlot[] - ): Promise => { + ): Promise => { + if (makers.length === 0) { + return undefined; + } ixs.push( await this.driftClient.getFillSpotOrderIx( new PublicKey(takerUserPubKey), @@ -1011,6 +1024,15 @@ export class SpotFillerMultithreaded { await this.driftClient.getRevertFillIx(user.userAccountPublicKey) ); } + + ixs.push( + new TransactionInstruction({ + programId: MEMO_PROGRAM_ID, + keys: [], + data: Buffer.from(`mm-spot ${fillTxId} ${makers.length}`, 'utf-8'), + }) + ); + const simResult = await simulateAndGetTxWithCUs({ ixs, connection: this.driftClient.connection, @@ -1042,6 +1064,9 @@ export class SpotFillerMultithreaded { }; let simResult = await buildTxWithMakerInfos(makerInfosToUse); + if (simResult === undefined) { + return true; + } let txAccounts = simResult.tx.message.getAccountKeys({ addressLookupTableAccounts: this.lookupTableAccounts, }).length; @@ -1054,9 +1079,6 @@ export class SpotFillerMultithreaded { ); makerInfosToUse = makerInfosToUse.slice(0, makerInfosToUse.length - 1); simResult = await buildTxWithMakerInfos(makerInfosToUse); - txAccounts = simResult.tx.message.getAccountKeys({ - addressLookupTableAccounts: this.lookupTableAccounts, - }).length; } if (makerInfosToUse.length === 0) { @@ -1065,6 +1087,16 @@ export class SpotFillerMultithreaded { ); return true; } + if (simResult === undefined) { + logger.error( + `No simResult after ${attempt} attempts (fillTxId: ${fillTxId})` + ); + return true; + } + + txAccounts = simResult.tx.message.getAccountKeys({ + addressLookupTableAccounts: this.lookupTableAccounts, + }).length; logger.info( `tryFillMultiMakerSpotNodes estimated CUs: ${simResult.cuEstimate} (fillTxId: ${fillTxId})` @@ -1197,7 +1229,9 @@ export class SpotFillerMultithreaded { units: 1_400_000, }), ]; - if (!buildForBundle) { + if (buildForBundle) { + ixs.push(this.bundleSender!.getTipIx()); + } else { const priorityFee = Math.floor( this.priorityFeeSubscriber.getPriorityFees( 'spot', @@ -1230,6 +1264,15 @@ export class SpotFillerMultithreaded { await this.driftClient.getRevertFillIx(user.userAccountPublicKey) ); } + + ixs.push( + new TransactionInstruction({ + programId: MEMO_PROGRAM_ID, + keys: [], + data: Buffer.from(`ff-spot ${fillTxId}`, 'utf-8'), + }) + ); + const simResult = await simulateAndGetTxWithCUs({ ixs, connection: this.driftClient.connection, diff --git a/src/utils.ts b/src/utils.ts index 93aa21e4..8c6146d7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -70,6 +70,10 @@ export const TOKEN_FAUCET_PROGRAM_ID = new PublicKey( 'V4v1mQiAdLz4qwckEb45WqHYceYizoib39cDBHSWfaB' ); +export const MEMO_PROGRAM_ID = new PublicKey( + 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr' +); + const JUPITER_SLIPPAGE_BPS = 100; export const PRIORITY_FEE_SERVER_RATE_LIMIT_PER_MIN = 300;