diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 8daca5e2..7026eb42 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -65,4 +65,6 @@ jobs: kubectl get deployments -n $BRANCH_NAME -o name | grep filler | xargs -I {} kubectl rollout restart {} -n $BRANCH_NAME kubectl rollout restart -n $BRANCH_NAME deployment/liquidator-bot kubectl rollout restart -n $BRANCH_NAME deployment/pyth-cranker-bot - kubectl rollout restart -n $BRANCH_NAME deployment/switchboard-cranker-bot \ No newline at end of file + kubectl rollout restart -n $BRANCH_NAME deployment/switchboard-cranker-bot + kubectl rollout restart -n $BRANCH_NAME deployment/swift-taker-example-bot + kubectl rollout restart -n $BRANCH_NAME deployment/swift-maker-example-bot diff --git a/package.json b/package.json index 473d9e2a..d32263d6 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "main": "lib/index.js", "license": "Apache-2.0", "dependencies": { - "@drift-labs/jit-proxy": "0.12.7", - "@drift-labs/sdk": "2.104.0-beta.12", + "@drift-labs/jit-proxy": "0.12.15", + "@drift-labs/sdk": "2.104.0-beta.28", "@opentelemetry/api": "1.7.0", "@opentelemetry/auto-instrumentations-node": "0.31.2", "@opentelemetry/exporter-prometheus": "0.31.0", diff --git a/src/bots/filler.ts b/src/bots/filler.ts index 4c4a4eea..d6d90130 100644 --- a/src/bots/filler.ts +++ b/src/bots/filler.ts @@ -89,7 +89,7 @@ import { validRebalanceSettledPnlThreshold, } from '../utils'; import { selectMakers } from '../makerSelection'; -import { BundleSender } from '../bundleSender'; +import { BundleSender, JITO_METRIC_TYPES } from '../bundleSender'; import { Metrics } from '../metrics'; import { LRUCache } from 'lru-cache'; import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes'; @@ -147,11 +147,6 @@ enum METRIC_TYPES { simulate_tx_duration_histogram = 'simulate_tx_duration_histogram', expired_nodes_set_size = 'expired_nodes_set_size', - jito_bundles_accepted = 'jito_bundles_accepted', - jito_bundles_simulation_failure = 'jito_simulation_failure', - jito_dropped_bundle = 'jito_dropped_bundle', - jito_landed_tips = 'jito_landed_tips', - jito_bundle_count = 'jito_bundle_count', clock_subscriber_ts = 'clock_subscriber_ts', wall_clock_ts = 'wall_clock_ts', } @@ -225,6 +220,7 @@ export class FillerBot extends TxThreaded implements Bot { protected mutexBusyCounter?: CounterValue; protected attemptedTriggersCounter?: CounterValue; protected txSimErrorCounter?: CounterValue; + protected jitoConnectedGauge?: GaugeValue; protected jitoBundlesAcceptedGauge?: GaugeValue; protected jitoBundlesSimulationFailureGauge?: GaugeValue; protected jitoDroppedBundleGauge?: GaugeValue; @@ -450,24 +446,28 @@ export class FillerBot extends TxThreaded implements Bot { METRIC_TYPES.tx_sim_error_count, 'Count of errors from simulating transactions' ); + this.jitoConnectedGauge = this.metrics.addGauge( + JITO_METRIC_TYPES.jito_connected, + 'Whether the jito bundle sender is connected' + ); this.jitoBundlesAcceptedGauge = this.metrics.addGauge( - METRIC_TYPES.jito_bundles_accepted, + JITO_METRIC_TYPES.jito_bundles_accepted, 'Count of jito bundles that were accepted' ); this.jitoBundlesSimulationFailureGauge = this.metrics.addGauge( - METRIC_TYPES.jito_bundles_simulation_failure, + JITO_METRIC_TYPES.jito_bundles_simulation_failure, 'Count of jito bundles that failed simulation' ); this.jitoDroppedBundleGauge = this.metrics.addGauge( - METRIC_TYPES.jito_dropped_bundle, + JITO_METRIC_TYPES.jito_dropped_bundle, 'Count of jito bundles that were dropped' ); this.jitoLandedTipsGauge = this.metrics.addGauge( - METRIC_TYPES.jito_landed_tips, + JITO_METRIC_TYPES.jito_landed_tips, 'Gauge of historic bundle tips that landed' ); this.jitoBundleCount = this.metrics.addGauge( - METRIC_TYPES.jito_bundle_count, + JITO_METRIC_TYPES.jito_bundle_count, 'Count of jito bundles that were sent, and their status' ); this.clockSubscriberTs = this.metrics.addGauge( @@ -1453,15 +1453,16 @@ export class FillerBot extends TxThreaded implements Bot { marketType, } = await this.getNodeFillInfo(nodeToFill); - logger.info( - logMessageForNodeToFill( - nodeToFill, - takerUserPubKey, - takerUserSlot, - makerInfos, - this.getMaxSlot(), - `Filling multi maker perp node with ${nodeToFill.makerNodes.length} makers (fillTxId: ${fillTxId})` - ) + logMessageForNodeToFill( + nodeToFill, + takerUserPubKey, + takerUserSlot, + makerInfos, + this.getMaxSlot(), + fillTxId, + 'multiMakerPerpFill', + this.revertOnFailure ?? false, + false ); if (!isVariant(marketType, 'perp')) { @@ -1645,7 +1646,7 @@ export class FillerBot extends TxThreaded implements Bot { const nodesSent: Array = []; const fillTxId = this.fillTxId++; - for (const [idx, nodeToFill] of nodesToFill.entries()) { + for (const [_idx, nodeToFill] of nodesToFill.entries()) { let ixs: Array = [ ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000, @@ -1681,15 +1682,16 @@ export class FillerBot extends TxThreaded implements Bot { marketType, } = await this.getNodeFillInfo(nodeToFill); - logger.info( - logMessageForNodeToFill( - nodeToFill, - takerUserPubKey, - takerUserSlot, - makerInfos, - this.getMaxSlot(), - `Filling perp node ${idx} (fillTxId: ${fillTxId})` - ) + logMessageForNodeToFill( + nodeToFill, + takerUserPubKey, + takerUserSlot, + makerInfos, + this.getMaxSlot(), + fillTxId, + 'fillPerpNode', + this.revertOnFailure ?? false, + false ); this.logSlots(); @@ -2260,6 +2262,9 @@ export class FillerBot extends TxThreaded implements Bot { } return slotsUntilJito < SLOTS_UNTIL_JITO_LEADER_TO_SEND; } + if (!this.bundleSender?.connected()) { + return false; + } return true; } diff --git a/src/bots/spotFiller.ts b/src/bots/spotFiller.ts index 2e06af87..34ae5d59 100644 --- a/src/bots/spotFiller.ts +++ b/src/bots/spotFiller.ts @@ -84,7 +84,7 @@ import { validMinimumGasAmount, validRebalanceSettledPnlThreshold, } from '../utils'; -import { BundleSender } from '../bundleSender'; +import { JITO_METRIC_TYPES, BundleSender } from '../bundleSender'; import { CACHED_BLOCKHASH_OFFSET, CONFIRM_TX_INTERVAL_MS, @@ -139,11 +139,6 @@ enum METRIC_TYPES { simulate_tx_duration_histogram = 'simulate_tx_duration_histogram', expired_nodes_set_size = 'expired_nodes_set_size', - jito_bundles_accepted = 'jito_bundles_accepted', - jito_bundles_simulation_failure = 'jito_simulation_failure', - jito_dropped_bundle = 'jito_dropped_bundle', - jito_landed_tips = 'jito_landed_tips', - jito_bundle_count = 'jito_bundle_count', clock_subscriber_ts = 'clock_subscriber_ts', wall_clock_ts = 'wall_clock_ts', } @@ -309,6 +304,7 @@ export class SpotFillerBot implements Bot { protected pendingTxSigsLoopRateLimitedCounter?: CounterValue; protected evictedPendingTxSigsToConfirmCounter?: CounterValue; protected expiredNodesSetSize?: GaugeValue; + protected jitoConnectedGauge?: GaugeValue; protected jitoBundlesAcceptedGauge?: GaugeValue; protected jitoBundlesSimulationFailureGauge?: GaugeValue; protected jitoDroppedBundleGauge?: GaugeValue; @@ -583,24 +579,28 @@ export class SpotFillerBot implements Bot { METRIC_TYPES.expired_nodes_set_size, 'Count of nodes that are expired' ); + this.jitoConnectedGauge = this.metrics.addGauge( + JITO_METRIC_TYPES.jito_connected, + 'Whether the jito bundle sender is connected' + ); this.jitoBundlesAcceptedGauge = this.metrics.addGauge( - METRIC_TYPES.jito_bundles_accepted, + JITO_METRIC_TYPES.jito_bundles_accepted, 'Count of jito bundles that were accepted' ); this.jitoBundlesSimulationFailureGauge = this.metrics.addGauge( - METRIC_TYPES.jito_bundles_simulation_failure, + JITO_METRIC_TYPES.jito_bundles_simulation_failure, 'Count of jito bundles that failed simulation' ); this.jitoDroppedBundleGauge = this.metrics.addGauge( - METRIC_TYPES.jito_dropped_bundle, + JITO_METRIC_TYPES.jito_dropped_bundle, 'Count of jito bundles that were dropped' ); this.jitoLandedTipsGauge = this.metrics.addGauge( - METRIC_TYPES.jito_landed_tips, + JITO_METRIC_TYPES.jito_landed_tips, 'Gauge of historic bundle tips that landed' ); this.jitoBundleCount = this.metrics.addGauge( - METRIC_TYPES.jito_bundle_count, + JITO_METRIC_TYPES.jito_bundle_count, 'Count of jito bundles that were sent, and their status' ); this.clockSubscriberTs = this.metrics.addGauge( @@ -1571,6 +1571,9 @@ export class SpotFillerBot implements Bot { } return slotsUntilJito < SLOTS_UNTIL_JITO_LEADER_TO_SEND; } + if (!this.bundleSender?.connected()) { + return false; + } return true; } @@ -1764,17 +1767,17 @@ export class SpotFillerBot implements Bot { marketType, } = await this.getNodeFillInfo(nodeToFill); - logger.info( - logMessageForNodeToFill( - nodeToFill, - takerUserPubKey, - takerUserSlot, - makerInfos, - this.getMaxSlot(), - `Filling multi maker spot node with ${nodeToFill.makerNodes.length} makers (fillTxId: ${fillTxId})`, - spotPrecision, - 'SHOULD_NOT_HAVE_NO_MAKERS' - ) + logMessageForNodeToFill( + nodeToFill, + takerUserPubKey, + takerUserSlot, + makerInfos, + this.getMaxSlot(), + fillTxId, + 'multiMakerSpotFill', + this.revertOnFailure ?? false, + false, + spotPrecision ); if (!isVariant(marketType, 'spot')) { @@ -2015,17 +2018,17 @@ export class SpotFillerBot implements Bot { } } - logger.info( - logMessageForNodeToFill( - nodeToFill, - takerUserPubKey, - takerUserSlot, - makerInfos, - this.getMaxSlot(), - `Filling spot node with ${nodeToFill.makerNodes.length} makers (fillTxId: ${fillTxId})`, - spotMarketPrecision, - fallbackSource as string - ) + logMessageForNodeToFill( + nodeToFill, + takerUserPubKey, + takerUserSlot, + makerInfos, + this.getMaxSlot(), + fillTxId, + 'fillSpotNode', + this.revertOnFailure ?? false, + false, + spotMarketPrecision ); const ixs = [ diff --git a/src/bots/switchboardCranker.ts b/src/bots/switchboardCranker.ts index 3acb226b..5db1a91c 100644 --- a/src/bots/switchboardCranker.ts +++ b/src/bots/switchboardCranker.ts @@ -106,6 +106,16 @@ export class SwitchboardCrankerBot implements Bot { return recentBlockhash.blockhash; } + private shouldBuildForBundle(): boolean { + if (!this.globalConfig.useJito) { + return false; + } + if (!this.bundleSender?.connected()) { + return false; + } + return true; + } + async runCrankLoop() { const pullFeedAliases = chunks( shuffle(Object.keys(this.crankConfigs.pullFeedConfigs)), @@ -119,7 +129,8 @@ export class SwitchboardCrankerBot implements Bot { }), ]; - if (this.globalConfig.useJito) { + const shouldBuildForBundle = this.shouldBuildForBundle(); + if (shouldBuildForBundle) { ixs.push(this.bundleSender!.getTipIx()); } else { const priorityFees = @@ -143,7 +154,7 @@ export class SwitchboardCrankerBot implements Bot { await this.getBlockhashForTx() ); - if (this.globalConfig.useJito) { + if (shouldBuildForBundle) { tx.sign([ // @ts-ignore; this.driftClient.wallet.payer, diff --git a/src/bundleSender.ts b/src/bundleSender.ts index dfa53696..066eb4de 100644 --- a/src/bundleSender.ts +++ b/src/bundleSender.ts @@ -18,6 +18,7 @@ import { BundleResult } from 'jito-ts/dist/gen/block-engine/bundle'; import { LRUCache } from 'lru-cache'; import WebSocket from 'ws'; import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes'; +import { sleepMs } from './utils'; export const jitoBundlePriceEndpoint = 'ws://bundles-api-rest.jito.wtf/api/v1/bundles/tip_stream'; @@ -26,9 +27,17 @@ const logPrefix = '[BundleSender]'; const MS_DELAY_BEFORE_CHECK_INCLUSION = 30_000; const MAX_TXS_TO_CHECK = 50; const RECONNECT_DELAY_MS = 5000; -const MAX_RECONNECT_ATTEMPTS = 3; const RECONNECT_RESET_DELAY_MS = 30000; +export enum JITO_METRIC_TYPES { + jito_connected = 'jito_connected', + jito_bundles_accepted = 'jito_bundles_accepted', + jito_bundles_simulation_failure = 'jito_simulation_failure', + jito_dropped_bundle = 'jito_dropped_bundle', + jito_landed_tips = 'jito_landed_tips', + jito_bundle_count = 'jito_bundle_count', +} + export type TipStream = { time: string; ts: number; // millisecond timestamp @@ -85,6 +94,7 @@ export class BundleSender { private checkingSentTxs = false; private reconnectAttempts = 0; private lastReconnectTime = 0; + private reconnecting = false; // if there is a big difference, probably jito ws connection is bad, should resub private bundlesSent = 0; @@ -135,6 +145,18 @@ export class BundleSender { return this.nextJitoLeader.nextLeaderSlot - this.slotSubscriber.getSlot(); } + connected(): boolean { + return ( + // tip stream connected + this.ws !== undefined && + this.ws.readyState === WebSocket.OPEN && + // searcher client connected + this.searcherClient !== undefined && + // next jito leader is set + this.nextJitoLeader !== undefined + ); + } + getBundleStats(): BundleStats { return this.bundleStats; } @@ -269,21 +291,34 @@ export class BundleSender { connect(); } + private getBackoffTimeForReconnectAttempts(): number { + return Math.min( + RECONNECT_DELAY_MS * this.reconnectAttempts, + RECONNECT_RESET_DELAY_MS + ); + } + private async connectSearcherClient() { const now = Date.now(); - if (now - this.lastReconnectTime > RECONNECT_RESET_DELAY_MS) { - this.reconnectAttempts = 0; + logger.info( + `${logPrefix}: connecting searcherClient. lastReconnectTime: ${ + now - this.lastReconnectTime + }ms ago. reconnecting: ${this.reconnecting}. reconnectAttempts: ${ + this.reconnectAttempts + }` + ); + if (this.reconnecting) { + logger.info(`${logPrefix}: already reconnecting, skipping...`); + return; } - if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) { - logger.error( - `${logPrefix}: Max reconnection attempts (${MAX_RECONNECT_ATTEMPTS}) reached. Waiting ${RECONNECT_RESET_DELAY_MS}ms before trying again` - ); - return; + if (now - this.lastReconnectTime > RECONNECT_RESET_DELAY_MS) { + this.reconnectAttempts = 0; } try { + this.reconnecting = true; this.searcherClient = searcherClient( this.jitoBlockEngineUrl, this.jitoAuthKeypair, @@ -314,9 +349,7 @@ export class BundleSender { this.reconnectAttempts++; this.lastReconnectTime = Date.now(); - await new Promise((resolve) => - setTimeout(resolve, RECONNECT_DELAY_MS) - ); + await sleepMs(this.getBackoffTimeForReconnectAttempts()); await this.connectSearcherClient(); } ); @@ -326,6 +359,8 @@ export class BundleSender { const tipAccounts = await this.searcherClient.getTipAccounts(); this.jitoTipAccounts = tipAccounts.map((k) => new PublicKey(k)); + + this.reconnecting = false; } catch (e) { const err = e as Error; logger.error( @@ -334,8 +369,10 @@ export class BundleSender { this.reconnectAttempts++; this.lastReconnectTime = Date.now(); - await new Promise((resolve) => setTimeout(resolve, RECONNECT_DELAY_MS)); + await sleepMs(this.getBackoffTimeForReconnectAttempts()); await this.connectSearcherClient(); + } finally { + this.reconnecting = false; } } @@ -454,7 +491,7 @@ export class BundleSender { this.failBundleCount }, totalLandedTxs: ${this.countLandedBundles}, totalDroppedTxs: ${ this.countDroppedbundles - }, currentTipAmount: ${this.calculateCurrentTipAmount()}, lastJitoTipStream: ${JSON.stringify( + }, currentTipAmount: ${this.calculateCurrentTipLamports()}, lastJitoTipStream: ${JSON.stringify( this.lastTipStream )} bundle stats: ${JSON.stringify(this.bundleStats)}` ); @@ -486,12 +523,12 @@ export class BundleSender { /** * - * @returns current tip based on running score + * @returns current tip based on running score in lamports */ - calculateCurrentTipAmount() { + public calculateCurrentTipLamports(): number { return Math.floor( Math.max( - (this.lastTipStream?.landed_tips_25th_percentile ?? 0) * + (this.lastTipStream?.landed_tips_50th_percentile ?? 0) * LAMPORTS_PER_SOL, this.minBundleTip, Math.min( @@ -519,7 +556,7 @@ export class BundleSender { ]; } if (tipAmount === undefined) { - tipAmount = this.calculateCurrentTipAmount(); + tipAmount = this.calculateCurrentTipLamports(); } const tipIx = SystemProgram.transfer({ fromPubkey: this.tipPayerKeypair.publicKey, @@ -581,7 +618,7 @@ export class BundleSender { ]; b = b.addTipTx( this.tipPayerKeypair!, - this.calculateCurrentTipAmount(), + this.calculateCurrentTipLamports(), tipAccountToUse!, signedTxs[0].message.recentBlockhash ); diff --git a/src/bundleSenderStrategy.ts b/src/bundleSenderStrategy.ts deleted file mode 100644 index 95574322..00000000 --- a/src/bundleSenderStrategy.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { LAMPORTS_PER_SOL } from '@solana/web3.js'; -import { TipStream } from './bundleSender'; - -/** - * A strategy for determining the amount of tip - * @param latestTipStream - The latest tip stream. - * @param failBundleCount - Running count of bundles that have failed to land. - * @returns The number of bundles to send. - */ -export type BundleSenderStrategy = ( - latestTipStream: TipStream, - failBundleCount: number -) => number; - -const minBundleTip = 10_000; // cant be lower than this -const maxBundleTip = 100_000; -const maxFailBundleCount = 100; // at 100 failed txs, can expect tip to become maxBundleTip -const tipMultiplier = 3; // bigger == more superlinear, delay the ramp up to prevent overpaying too soon -export const exponentialBundleTip: BundleSenderStrategy = ( - latestTipStream: TipStream, - failBundleCount: number -) => { - return Math.floor( - Math.max( - latestTipStream.landed_tips_25th_percentile ?? 0 * LAMPORTS_PER_SOL, - minBundleTip, - Math.min( - maxBundleTip, - Math.pow(failBundleCount / maxFailBundleCount, tipMultiplier) * - maxBundleTip - ) - ) - ); -}; diff --git a/src/experimental-bots/filler/fillerMultithreaded.ts b/src/experimental-bots/filler/fillerMultithreaded.ts index 38255ec9..5b52ac85 100644 --- a/src/experimental-bots/filler/fillerMultithreaded.ts +++ b/src/experimental-bots/filler/fillerMultithreaded.ts @@ -31,7 +31,7 @@ import { UserStatsMap, } from '@drift-labs/sdk'; import { FillerMultiThreadedConfig, GlobalConfig } from '../../config'; -import { BundleSender } from '../../bundleSender'; +import { JITO_METRIC_TYPES, BundleSender } from '../../bundleSender'; import { AddressLookupTableAccount, ComputeBudgetProgram, @@ -154,12 +154,6 @@ enum METRIC_TYPES { estimated_tx_cu_histogram = 'estimated_tx_cu_histogram', simulate_tx_duration_histogram = 'simulate_tx_duration_histogram', expired_nodes_set_size = 'expired_nodes_set_size', - - jito_bundles_accepted = 'jito_bundles_accepted', - jito_bundles_simulation_failure = 'jito_simulation_failure', - jito_dropped_bundle = 'jito_dropped_bundle', - jito_landed_tips = 'jito_landed_tips', - jito_bundle_count = 'jito_bundle_count', } const getNodeToTriggerSignature = (node: SerializedNodeToTrigger): string => { @@ -237,6 +231,7 @@ export class FillerMultithreaded { protected pendingTxSigsLoopRateLimitedCounter?: CounterValue; protected evictedPendingTxSigsToConfirmCounter?: CounterValue; protected expiredNodesSetSize?: GaugeValue; + protected jitoConnectedGauge?: GaugeValue; protected jitoBundlesAcceptedGauge?: GaugeValue; protected jitoBundlesSimulationFailureGauge?: GaugeValue; protected jitoDroppedBundleGauge?: GaugeValue; @@ -692,24 +687,28 @@ export class FillerMultithreaded { METRIC_TYPES.expired_nodes_set_size, 'Count of nodes that are expired' ); + this.jitoConnectedGauge = this.metrics.addGauge( + JITO_METRIC_TYPES.jito_connected, + 'Whether the jito bundle sender is connected' + ); this.jitoBundlesAcceptedGauge = this.metrics.addGauge( - METRIC_TYPES.jito_bundles_accepted, + JITO_METRIC_TYPES.jito_bundles_accepted, 'Count of jito bundles that were accepted' ); this.jitoBundlesSimulationFailureGauge = this.metrics.addGauge( - METRIC_TYPES.jito_bundles_simulation_failure, + JITO_METRIC_TYPES.jito_bundles_simulation_failure, 'Count of jito bundles that failed simulation' ); this.jitoDroppedBundleGauge = this.metrics.addGauge( - METRIC_TYPES.jito_dropped_bundle, + JITO_METRIC_TYPES.jito_dropped_bundle, 'Count of jito bundles that were dropped' ); this.jitoLandedTipsGauge = this.metrics.addGauge( - METRIC_TYPES.jito_landed_tips, + JITO_METRIC_TYPES.jito_landed_tips, 'Gauge of historic bundle tips that landed' ); this.jitoBundleCount = this.metrics.addGauge( - METRIC_TYPES.jito_bundle_count, + JITO_METRIC_TYPES.jito_bundle_count, 'Count of jito bundles that were sent, and their status' ); @@ -733,6 +732,15 @@ export class FillerMultithreaded { const user = this.driftClient.getUser(this.subaccount); const bundleStats = this.bundleSender?.getBundleStats(); if (bundleStats) { + this.jitoConnectedGauge?.setLatestValue( + this.bundleSender?.connected() ? 1 : 0, + { + ...metricAttrFromUserAccount( + user.userAccountPublicKey, + user.getUserAccount() + ), + } + ); this.jitoBundlesAcceptedGauge?.setLatestValue(bundleStats.accepted, { ...metricAttrFromUserAccount( user.userAccountPublicKey, @@ -1141,6 +1149,9 @@ export class FillerMultithreaded { } return slotsUntilJito < SLOTS_UNTIL_JITO_LEADER_TO_SEND; } + if (!this.bundleSender?.connected()) { + return false; + } return true; } @@ -1170,13 +1181,8 @@ export class FillerMultithreaded { `${logPrefix} Filtered down to ${filteredTriggerableNodes.length} triggerable nodes...` ); - const buildForBundle = this.shouldBuildForBundle(); - try { - await this.executeTriggerablePerpNodes( - filteredTriggerableNodes, - !!buildForBundle - ); + await this.executeTriggerablePerpNodes(filteredTriggerableNodes); } catch (e) { if (e instanceof Error) { logger.error( @@ -1208,10 +1214,7 @@ export class FillerMultithreaded { return true; } - async executeTriggerablePerpNodes( - nodesToTrigger: SerializedNodeToTrigger[], - buildForBundle: boolean - ) { + async executeTriggerablePerpNodes(nodesToTrigger: SerializedNodeToTrigger[]) { const user = this.driftClient.getUser(this.subaccount); for (const nodeToTrigger of nodesToTrigger) { let ixs = [ @@ -1220,21 +1223,24 @@ export class FillerMultithreaded { }), ]; + const priorityFeePrice = Math.floor( + Math.max( + ...nodesToTrigger.map((node: SerializedNodeToTrigger) => { + return this.priorityFeeSubscriber.getPriorityFees( + 'perp', + node.node.order.marketIndex + )!.medium; + }) + ) * this.driftClient.txSender.getSuggestedPriorityFeeMultiplier() + ); + const buildForBundle = this.shouldBuildForBundle(); + if (buildForBundle) { ixs.push(this.bundleSender!.getTipIx()); } else { ixs.push( ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: Math.floor( - Math.max( - ...nodesToTrigger.map((node: SerializedNodeToTrigger) => { - return this.priorityFeeSubscriber.getPriorityFees( - 'perp', - node.node.order.marketIndex - )!.medium; - }) - ) * this.driftClient.txSender.getSuggestedPriorityFeeMultiplier() - ), + microLamports: priorityFeePrice, }) ); } @@ -1255,11 +1261,16 @@ export class FillerMultithreaded { if (this.pythPriceSubscriber) { const pythIxs = await this.getPythIxsFromNode(nodeToTrigger); ixs.push(...pythIxs); + // do not strip the last ix if we have pyth ixs removeLastIxPostSim = false; } const nodeSignature = getNodeToTriggerSignature(nodeToTrigger); if (this.seenTriggerableOrders.has(nodeSignature)) { + if (!this.pythPriceSubscriber) { + // no pyth subscriber, tx will be empty + return; + } logger.info( `${logPrefix} already triggered order (account: ${ nodeToTrigger.node.userAccount @@ -1288,7 +1299,7 @@ export class FillerMultithreaded { const txSize = getSizeOfTransaction(ixs, true, this.lookupTableAccounts); if (txSize > PACKET_DATA_SIZE) { - logger.info(`tx too large, removing pyth ixs.`); + logger.warn(`tx too large, removing pyth ixs.`); ixs = removePythIxs(ixs); } @@ -1320,7 +1331,7 @@ export class FillerMultithreaded { }); logger.info( - `executeTriggerablePerpNodesForMarket (${nodeSignature}) estimated CUs: ${simResult.cuEstimate}, finalIxCount: ${ixs.length}). revertTx: ${this.revertOnFailure}}` + `executeTriggerablePerpNodesForMarket (${nodeSignature}) estimated CUs: ${simResult.cuEstimate}, finalIxCount: ${ixs.length}). revertTx: ${this.revertOnFailure}}, buildForBundle: ${buildForBundle}` ); if (simResult.simError) { @@ -1340,9 +1351,10 @@ export class FillerMultithreaded { .sendTransaction(simResult.tx) .then((txSig) => { logger.info( - `Triggered user (account: ${nodeToTrigger.node.userAccount.toString()}) order: ${nodeToTrigger.node.order.orderId.toString()}` + `${logPrefix} Triggered user (account: ${nodeToTrigger.node.userAccount.toString()}) order: ${nodeToTrigger.node.order.orderId.toString()} Tx: ${ + txSig.txSig + }` ); - logger.info(`${logPrefix} Tx: ${txSig.txSig}`); }) .catch((error) => { nodeToTrigger.node.haveTrigger = false; @@ -1407,13 +1419,8 @@ export class FillerMultithreaded { `${logPrefix} Filtered down to ${filteredFillableNodes.length} fillable nodes...` ); - const buildForBundle = this.shouldBuildForBundle(); - try { - await this.executeFillablePerpNodes( - filteredFillableNodes, - !!buildForBundle - ); + await this.executeFillablePerpNodes(filteredFillableNodes); } catch (e) { if (e instanceof Error) { logger.error( @@ -1491,10 +1498,7 @@ export class FillerMultithreaded { return true; } - async executeFillablePerpNodes( - nodesToFill: NodeToFillWithBuffer[], - buildForBundle: boolean - ) { + async executeFillablePerpNodes(nodesToFill: NodeToFillWithBuffer[]) { for (const node of nodesToFill) { if (this.seenFillableOrders.has(getNodeToFillSignature(node))) { logger.debug( @@ -1505,29 +1509,21 @@ export class FillerMultithreaded { ); continue; } + this.seenFillableOrders.add(getNodeToFillSignature(node)); if (node.makerNodes.length > 1) { - this.tryFillMultiMakerPerpNodes(node, buildForBundle); + this.tryFillMultiMakerPerpNodes(node); } else { - this.tryFillPerpNode(node, buildForBundle); + this.tryFillPerpNode(node); } } } - protected async tryFillMultiMakerPerpNodes( - nodeToFill: NodeToFillWithBuffer, - buildForBundle: boolean - ) { + protected async tryFillMultiMakerPerpNodes(nodeToFill: NodeToFillWithBuffer) { const fillTxId = this.fillTxId++; let nodeWithMakerSet = nodeToFill; - while ( - !(await this.fillMultiMakerPerpNodes( - fillTxId, - nodeWithMakerSet, - buildForBundle - )) - ) { + while (!(await this.fillMultiMakerPerpNodes(fillTxId, nodeWithMakerSet))) { const newMakerSet = nodeWithMakerSet.makerNodes .sort(() => 0.5 - Math.random()) .slice(0, Math.ceil(nodeWithMakerSet.makerNodes.length / 2)); @@ -1548,8 +1544,7 @@ export class FillerMultithreaded { private async fillMultiMakerPerpNodes( fillTxId: number, - nodeToFill: NodeToFillWithBuffer, - buildForBundle: boolean + nodeToFill: NodeToFillWithBuffer ): Promise { let ixs: Array = [ ComputeBudgetProgram.setComputeUnitLimit({ @@ -1578,18 +1573,20 @@ export class FillerMultithreaded { removeLastIxPostSim = false; } + const priorityFeePrice = Math.floor( + this.priorityFeeSubscriber.getPriorityFees( + 'perp', + nodeToFill.node.order!.marketIndex! + )!.high * this.driftClient.txSender.getSuggestedPriorityFeeMultiplier() + ); + const buildForBundle = this.shouldBuildForBundle(); + if (buildForBundle) { ixs.push(this.bundleSender!.getTipIx()); } else { ixs.push( getPriorityFeeInstruction( - Math.floor( - this.priorityFeeSubscriber.getPriorityFees( - 'perp', - nodeToFill.node.order!.marketIndex! - )!.high * - this.driftClient.txSender.getSuggestedPriorityFeeMultiplier() - ), + priorityFeePrice, this.driftClient.getOracleDataForPerpMarket(0).price, this.config.bidToFillerReward ? fillerRewardEstimate : undefined, this.globalConfig.priorityFeeMultiplier @@ -1597,15 +1594,16 @@ export class FillerMultithreaded { ); } - logger.info( - logMessageForNodeToFill( - nodeToFill, - takerUserPubKey, - takerUserSlot, - makerInfos, - this.slotSubscriber.getSlot(), - `${logPrefix} Filling multi maker perp node with ${nodeToFill.makerNodes.length} makers (fillTxId: ${fillTxId})` - ) + logMessageForNodeToFill( + nodeToFill, + takerUserPubKey, + takerUserSlot, + makerInfos, + this.slotSubscriber.getSlot(), + fillTxId, + 'multiMakerFill', + this.revertOnFailure ?? false, + removeLastIxPostSim ?? false ); if (!isVariant(marketType, 'perp')) { @@ -1648,7 +1646,7 @@ export class FillerMultithreaded { true, this.lookupTableAccounts ); - if (txSize > PACKET_DATA_SIZE) { + if (txSize > PACKET_DATA_SIZE && this.pythPriceSubscriber) { logger.info(`tx too large, removing pyth ixs. keys: ${ixs.map((ix) => ix.keys.map((key) => key.pubkey.toString()))} total number of maker positions: ${makerInfos.reduce( @@ -1780,10 +1778,15 @@ export class FillerMultithreaded { return true; } - protected async tryFillPerpNode( - nodeToFill: NodeToFillWithBuffer, - buildForBundle: boolean - ) { + protected async tryFillPerpNode(nodeToFill: NodeToFillWithBuffer) { + const priorityFeePrice = Math.floor( + this.priorityFeeSubscriber.getPriorityFees( + 'perp', + nodeToFill.node.order!.marketIndex! + )!.high * this.driftClient.txSender.getSuggestedPriorityFeeMultiplier() + ); + const buildForBundle = this.shouldBuildForBundle(); + let ixs = [ ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000, @@ -1813,13 +1816,7 @@ export class FillerMultithreaded { } else { ixs.push( getPriorityFeeInstruction( - Math.floor( - this.priorityFeeSubscriber.getPriorityFees( - 'perp', - nodeToFill.node.order!.marketIndex! - )!.high * - this.driftClient.txSender.getSuggestedPriorityFeeMultiplier() - ), + priorityFeePrice, this.driftClient.getOracleDataForPerpMarket(0).price, this.config.bidToFillerReward ? fillerRewardEstimate : undefined, this.globalConfig.priorityFeeMultiplier @@ -1827,15 +1824,16 @@ export class FillerMultithreaded { ); } - logger.info( - logMessageForNodeToFill( - nodeToFill, - takerUserPubKey, - takerUserSlot, - makerInfos, - this.slotSubscriber.getSlot(), - `Filling perp node (fillTxId: ${fillTxId})` - ) + logMessageForNodeToFill( + nodeToFill, + takerUserPubKey, + takerUserSlot, + makerInfos, + this.slotSubscriber.getSlot(), + fillTxId, + 'single', + this.revertOnFailure ?? false, + removeLastIxPostSim ?? false ); if (!isVariant(marketType, 'perp')) { @@ -2054,10 +2052,21 @@ export class FillerMultithreaded { chunk_size = marketIds.length / 2; } const settlePnlPromises: Array> = []; - const buildForBundle = this.shouldBuildForBundle(); for (let i = 0; i < marketIds.length; i += chunk_size) { const marketIdChunks = marketIds.slice(i, i + chunk_size); try { + const priorityFeePrice = Math.floor( + Math.max( + ...marketIdChunks.map((marketId) => { + return this.priorityFeeSubscriber.getPriorityFees( + 'perp', + marketId + )!.medium; + }) + ) * this.driftClient.txSender.getSuggestedPriorityFeeMultiplier() + ); + const buildForBundle = this.shouldBuildForBundle(); + const ixs = [ ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000, // will be overridden by simulateTx @@ -2069,17 +2078,7 @@ export class FillerMultithreaded { } else { ixs.push( ComputeBudgetProgram.setComputeUnitPrice({ - microLamports: Math.floor( - Math.max( - ...marketIdChunks.map((marketId) => { - return this.priorityFeeSubscriber.getPriorityFees( - 'perp', - marketId - )!.medium; - }) - ) * - this.driftClient.txSender.getSuggestedPriorityFeeMultiplier() - ), + microLamports: priorityFeePrice, }) ); } diff --git a/src/experimental-bots/spotFiller/spotFillerMultithreaded.ts b/src/experimental-bots/spotFiller/spotFillerMultithreaded.ts index a77c9c85..db87e563 100644 --- a/src/experimental-bots/spotFiller/spotFillerMultithreaded.ts +++ b/src/experimental-bots/spotFiller/spotFillerMultithreaded.ts @@ -37,7 +37,7 @@ import { } from '@solana/web3.js'; import { LRUCache } from 'lru-cache'; import { FillerMultiThreadedConfig, GlobalConfig } from '../../config'; -import { BundleSender } from '../../bundleSender'; +import { BundleSender, JITO_METRIC_TYPES } from '../../bundleSender'; import { logger } from '../../logger'; import { CounterValue, @@ -244,6 +244,7 @@ export class SpotFillerMultithreaded { protected pendingTxSigsLoopRateLimitedCounter?: CounterValue; protected evictedPendingTxSigsToConfirmCounter?: CounterValue; protected expiredNodesSetSize?: GaugeValue; + protected jitoConnectedGauge?: GaugeValue; protected jitoBundlesAcceptedGauge?: GaugeValue; protected jitoBundlesSimulationFailureGauge?: GaugeValue; protected jitoDroppedBundleGauge?: GaugeValue; @@ -976,17 +977,17 @@ export class SpotFillerMultithreaded { marketType, } = await this.getNodeFillInfo(nodeToFill); - logger.info( - logMessageForNodeToFill( - nodeToFill, - takerUserPubKey, - takerUserSlot, - makerInfos, - this.slotSubscriber.getSlot(), - `Filling multi maker spot node with ${nodeToFill.makerNodes.length} makers (fillTxId: ${fillTxId})`, - spotPrecision, - 'SHOULD_NOT_HAVE_NO_MAKERS' - ) + logMessageForNodeToFill( + nodeToFill, + takerUserPubKey, + takerUserSlot, + makerInfos, + this.slotSubscriber.getSlot(), + fillTxId, + 'multiMakerSpotFill', + this.revertOnFailure ?? false, + false, + spotPrecision ); if (!isVariant(marketType, 'spot')) { @@ -1205,17 +1206,17 @@ export class SpotFillerMultithreaded { } } - logger.info( - logMessageForNodeToFill( - nodeToFill, - takerUserPubKey, - takerUserSlot, - makerInfos, - this.slotSubscriber.getSlot(), - `Filling spot node with ${nodeToFill.makerNodes.length} makers (fillTxId: ${fillTxId})`, - spotMarketPrecision, - fallbackSource as string - ) + logMessageForNodeToFill( + nodeToFill, + takerUserPubKey, + takerUserSlot, + makerInfos, + this.slotSubscriber.getSlot(), + fillTxId, + 'fillSpotNode', + this.revertOnFailure ?? false, + false, + spotMarketPrecision ); const ixs = [ @@ -1546,24 +1547,28 @@ export class SpotFillerMultithreaded { METRIC_TYPES.expired_nodes_set_size, 'Count of nodes that are expired' ); + this.jitoConnectedGauge = this.metrics.addGauge( + JITO_METRIC_TYPES.jito_connected, + 'Whether the jito bundle sender is connected' + ); this.jitoBundlesAcceptedGauge = this.metrics.addGauge( - METRIC_TYPES.jito_bundles_accepted, + JITO_METRIC_TYPES.jito_bundles_accepted, 'Count of jito bundles that were accepted' ); this.jitoBundlesSimulationFailureGauge = this.metrics.addGauge( - METRIC_TYPES.jito_bundles_simulation_failure, + JITO_METRIC_TYPES.jito_bundles_simulation_failure, 'Count of jito bundles that failed simulation' ); this.jitoDroppedBundleGauge = this.metrics.addGauge( - METRIC_TYPES.jito_dropped_bundle, + JITO_METRIC_TYPES.jito_dropped_bundle, 'Count of jito bundles that were dropped' ); this.jitoLandedTipsGauge = this.metrics.addGauge( - METRIC_TYPES.jito_landed_tips, + JITO_METRIC_TYPES.jito_landed_tips, 'Gauge of historic bundle tips that landed' ); this.jitoBundleCount = this.metrics.addGauge( - METRIC_TYPES.jito_bundle_count, + JITO_METRIC_TYPES.jito_bundle_count, 'Count of jito bundles that were sent, and their status' ); @@ -1923,8 +1928,12 @@ export class SpotFillerMultithreaded { } return slotsUntilJito < SLOTS_UNTIL_JITO_LEADER_TO_SEND; } + if (!this.bundleSender?.connected()) { + return false; + } return true; } + protected async registerTxSigToConfirm( txSig: TransactionSignature, now: number, diff --git a/src/experimental-bots/swift/makerExample.ts b/src/experimental-bots/swift/makerExample.ts index 9950b6d4..b5f17377 100644 --- a/src/experimental-bots/swift/makerExample.ts +++ b/src/experimental-bots/swift/makerExample.ts @@ -147,8 +147,6 @@ export class SwiftMaker { } const ixs = await this.driftClient.getPlaceAndMakeSwiftPerpOrderIxs( - Buffer.from(order['swift_message'], 'base64'), - Buffer.from(order['swift_signature'], 'base64'), swiftOrderParamsBuf, Buffer.from(order['order_signature'], 'base64'), decodeUTF8(order['uuid']), diff --git a/src/experimental-bots/swift/takerExample.ts b/src/experimental-bots/swift/takerExample.ts index 890eb714..c1b152ee 100644 --- a/src/experimental-bots/swift/takerExample.ts +++ b/src/experimental-bots/swift/takerExample.ts @@ -1,11 +1,12 @@ import { - BASE_PRECISION, DriftClient, getMarketOrderParams, isVariant, MarketType, PositionDirection, digestSignature, + generateSwiftUuid, + BN, } from '@drift-labs/sdk'; import { RuntimeSpec } from 'src/metrics'; import * as axios from 'axios'; @@ -35,7 +36,7 @@ export class SwiftTaker { } async startInterval() { - const marketIndexes = [0, 1, 2, 3, 4, 5, 6]; + const marketIndexes = [0, 1, 2, 3, 5, 6]; this.interval = setInterval(async () => { await sleepMs(Math.random() * 1000); // Randomize for different grafana metrics const slot = await this.driftClient.connection.getSlot(); @@ -55,7 +56,9 @@ export class SwiftTaker { marketIndex, marketType: MarketType.PERP, direction, - baseAssetAmount: BASE_PRECISION, + baseAssetAmount: + this.driftClient.getPerpMarketAccount(marketIndex)!.amm + .minOrderSize, auctionStartPrice: isVariant(direction, 'long') ? lowPrice : highPrice, @@ -63,6 +66,8 @@ export class SwiftTaker { auctionDuration: 30, }), subAccountId: 0, + slot: new BN(slot), + uuid: generateSwiftUuid(), stopLossOrderParams: null, takeProfitOrderParams: null, }; diff --git a/src/utils.ts b/src/utils.ts index 0541ee24..b8bfe0f1 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -703,10 +703,12 @@ export function logMessageForNodeToFill( takerUserSlot: number, makerInfos: Array>, currSlot: number, - prefix?: string, - basePrecision: BN = BASE_PRECISION, - fallbackSource = 'vAMM' -): string { + fillId: number, + fillType: string, + revertOnFailure: boolean, + removeLastIxPreSim: boolean, + basePrecision: BN = BASE_PRECISION +) { const takerNode = node.node; const takerOrder = takerNode.order; if (!takerOrder) { @@ -719,51 +721,73 @@ export function logMessageForNodeToFill( ); } - let msg = ''; - if (prefix) { - msg += `${prefix}\n`; - } + // json log might be inefficient, but makes log parsing easier + logger.info( + 'fill attempt: ' + + JSON.stringify({ + marketIndex: takerOrder.marketIndex, + marketType: getVariant(takerOrder.marketType), + taker: takerUser, + takerOrderId: takerOrder.orderId, + takerOrderDirection: getVariant(takerOrder.direction), + takerSlot: takerUserSlot, + currSlot, + takerBaseAssetAmountFilled: convertToNumber( + takerOrder.baseAssetAmountFilled, + basePrecision + ), + takerBaseAssetAmount: convertToNumber( + takerOrder.baseAssetAmount, + basePrecision + ), + takerPrice: convertToNumber(takerOrder.price, PRICE_PRECISION), + takerOrderPrice: getVariant(takerOrder.orderType), + takerOrderPriceOffset: + takerOrder.oraclePriceOffset / PRICE_PRECISION.toNumber(), + makers: makerInfos.length, + fillType, + fillId, + revertOnFailure, + removeLastIxPreSim, + }) + ); - msg += `taker on market ${takerOrder.marketIndex}: ${takerUser}-${ - takerOrder.orderId - } (takerSlot: ${takerUserSlot}, currSlot: ${currSlot}) ${getVariant( - takerOrder.direction - )} ${convertToNumber( - takerOrder.baseAssetAmountFilled, - basePrecision - )}/${convertToNumber( - takerOrder.baseAssetAmount, - basePrecision - )} @ ${convertToNumber( - takerOrder.price, - PRICE_PRECISION - )} (orderType: ${getVariant(takerOrder.orderType)})\n`; - msg += `makers:\n`; if (makerInfos.length > 0) { for (let i = 0; i < makerInfos.length; i++) { const maker = makerInfos[i].data; const makerSlot = makerInfos[i].slot; const makerOrder = maker.order!; - msg += ` [${i}] market ${ - makerOrder.marketIndex - }: ${maker.maker.toBase58()}-${ - makerOrder.orderId - } (makerSlot: ${makerSlot}) ${getVariant( - makerOrder.direction - )} ${convertToNumber( - makerOrder.baseAssetAmountFilled, - basePrecision - )}/${convertToNumber( - makerOrder.baseAssetAmount, - basePrecision - )} @ ${convertToNumber(makerOrder.price, PRICE_PRECISION)} (offset: ${ - makerOrder.oraclePriceOffset / PRICE_PRECISION.toNumber() - }) (orderType: ${getVariant(makerOrder.orderType)})\n`; + logger.info( + 'fill attempt maker: ' + + JSON.stringify({ + marketIndex: makerOrder.marketIndex, + marketType: getVariant(makerOrder.marketType), + makerIdx: i, + maker: maker.maker.toBase58(), + makerSlot: makerSlot, + makerOrderId: makerOrder.orderId, + makerOrderType: getVariant(makerOrder.orderType), + makerOrderMarketIndex: makerOrder.marketIndex, + makerOrderDirection: getVariant(makerOrder.direction), + makerOrderBaseAssetAmountFilled: convertToNumber( + makerOrder.baseAssetAmountFilled, + basePrecision + ), + makerOrderBaseAssetAmount: convertToNumber( + makerOrder.baseAssetAmount, + basePrecision + ), + makerOrderPrice: convertToNumber(makerOrder.price, PRICE_PRECISION), + makerOrderPriceOffset: + makerOrder.oraclePriceOffset / PRICE_PRECISION.toNumber(), + fillType, + fillId, + revertOnFailure, + removeLastIxPreSim, + }) + ); } - } else { - msg += ` ${fallbackSource}`; } - return msg; } export function getTransactionAccountMetas( diff --git a/yarn.lock b/yarn.lock index 562593ee..825d76a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -108,28 +108,7 @@ superstruct "^0.15.4" toml "^3.0.0" -"@coral-xyz/anchor@0.28.0": - version "0.28.0" - resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.28.0.tgz#8345c3c9186a91f095f704d7b90cd256f7e8b2dc" - integrity sha512-kQ02Hv2ZqxtWP30WN1d4xxT4QqlOXYDxmEd3k/bbneqhV3X5QMO4LAtoUFs7otxyivOgoqam5Il5qx81FuI4vw== - dependencies: - "@coral-xyz/borsh" "^0.28.0" - "@solana/web3.js" "^1.68.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^6.3.0" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - superstruct "^0.15.4" - toml "^3.0.0" - -"@coral-xyz/anchor@^0.29.0": +"@coral-xyz/anchor@0.29.0", "@coral-xyz/anchor@^0.29.0": version "0.29.0" resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.29.0.tgz#bd0be95bedfb30a381c3e676e5926124c310ff12" integrity sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA== @@ -157,14 +136,6 @@ bn.js "^5.1.2" buffer-layout "^1.2.0" -"@coral-xyz/borsh@^0.28.0": - version "0.28.0" - resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.28.0.tgz#fa368a2f2475bbf6f828f4657f40a52102e02b6d" - integrity sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ== - dependencies: - bn.js "^5.1.2" - buffer-layout "^1.2.0" - "@coral-xyz/borsh@^0.29.0": version "0.29.0" resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.29.0.tgz#79f7045df2ef66da8006d47f5399c7190363e71f" @@ -197,44 +168,45 @@ enabled "2.0.x" kuler "^2.0.0" -"@drift-labs/jit-proxy@0.12.7": - version "0.12.7" - resolved "https://registry.yarnpkg.com/@drift-labs/jit-proxy/-/jit-proxy-0.12.7.tgz#9d4e3ad30c482e872078714020adac4d066b26d2" - integrity sha512-o14ahGh7lnL5p3+w6dlKxruFPPWtvdTe4eXjw0qOusrg+TiO4XwnBNr+h6p/n76MSAMPy7TWX340FhEvW9CDZw== +"@drift-labs/jit-proxy@0.12.15": + version "0.12.15" + resolved "https://registry.yarnpkg.com/@drift-labs/jit-proxy/-/jit-proxy-0.12.15.tgz#080afc74c9d7720ddf31f66c0a1999bb4eabe003" + integrity sha512-y+0FMjw6qG66sgYJIpGUviqXcXAnzyjMN15o+nDC+6NtK2LSHCkM3BPlZVOOK1IRQzhmw7Vk4f0zm2ba2vnhyA== dependencies: "@coral-xyz/anchor" "0.26.0" - "@drift-labs/sdk" "2.104.0-beta.12" + "@drift-labs/sdk" "2.104.0-beta.28" "@solana/web3.js" "1.91.7" -"@drift-labs/sdk@2.104.0-beta.12": - version "2.104.0-beta.12" - resolved "https://registry.yarnpkg.com/@drift-labs/sdk/-/sdk-2.104.0-beta.12.tgz#ddc8b9b832308e56411a20d35191fc8cdd5248fb" - integrity sha512-Ln0iFJ2Z5Mw7yldyLzKQWs/aPJo3Pt/SO7TeddssFznehIvo0OidgZ7KbFOUaobdoZC588abMHNGGEg93Ccc3A== +"@drift-labs/sdk@2.104.0-beta.28": + version "2.104.0-beta.28" + resolved "https://registry.yarnpkg.com/@drift-labs/sdk/-/sdk-2.104.0-beta.28.tgz#3fb1a7e2fbed76cc12a3dcfda5c01b9352b2a325" + integrity sha512-Py2qZabHgl+xvfkrcne8dxsCeEgNbddlc/y90lOQ3mjKyFAO06kKPEz6/Dej9ULackusTV20sY+4HnU3dEnJrQ== dependencies: - "@coral-xyz/anchor" "0.28.0" + "@coral-xyz/anchor" "0.29.0" "@coral-xyz/anchor-30" "npm:@coral-xyz/anchor@0.30.1" - "@ellipsis-labs/phoenix-sdk" "^1.4.2" + "@ellipsis-labs/phoenix-sdk" "1.4.5" "@grpc/grpc-js" "^1.8.0" "@openbook-dex/openbook-v2" "0.2.10" - "@project-serum/serum" "^0.13.38" + "@project-serum/serum" "0.13.65" "@pythnetwork/client" "2.5.3" - "@pythnetwork/price-service-sdk" "^1.7.1" - "@pythnetwork/pyth-solana-receiver" "^0.7.0" + "@pythnetwork/price-service-sdk" "1.7.1" + "@pythnetwork/pyth-solana-receiver" "0.7.0" "@solana/spl-token" "0.3.7" "@solana/web3.js" "1.92.3" "@switchboard-xyz/on-demand" "1.2.42" "@triton-one/yellowstone-grpc" "1.3.0" - anchor-bankrun "^0.3.0" - node-cache "^5.1.2" + anchor-bankrun "0.3.0" + nanoid "3.3.4" + node-cache "5.1.2" rpc-websockets "7.5.1" - solana-bankrun "^0.3.0" - strict-event-emitter-types "^2.0.0" + solana-bankrun "0.3.1" + strict-event-emitter-types "2.0.0" tweetnacl "1.0.3" - uuid "^8.3.2" - yargs "^17.7.2" - zstddec "^0.1.0" + uuid "8.3.2" + yargs "17.7.2" + zstddec "0.1.0" -"@ellipsis-labs/phoenix-sdk@^1.4.2": +"@ellipsis-labs/phoenix-sdk@1.4.5": version "1.4.5" resolved "https://registry.yarnpkg.com/@ellipsis-labs/phoenix-sdk/-/phoenix-sdk-1.4.5.tgz#42cf8de8463b32c910ab8844eae71ca082a6773a" integrity sha512-vEYgMXuV5/mpnpEi+VK4HO8f6SheHtVLdHHlULBiDN1eECYmL67gq+/cRV7Ar6jAQ7rJZL7xBxhbUW5kugMl6A== @@ -1072,7 +1044,7 @@ bn.js "^5.1.2" buffer-layout "^1.2.0" -"@project-serum/serum@0.13.65", "@project-serum/serum@^0.13.38": +"@project-serum/serum@0.13.65": version "0.13.65" resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.65.tgz#6d3cf07912f13985765237f053cca716fe84b0b0" integrity sha512-BHRqsTqPSfFB5p+MgI2pjvMBAQtO8ibTK2fYY96boIFkCI3TTwXDt2gUmspeChKO2pqHr5aKevmexzAcXxrSRA== @@ -1158,14 +1130,14 @@ ts-log "^2.2.4" ws "^8.6.0" -"@pythnetwork/price-service-sdk@*", "@pythnetwork/price-service-sdk@>=1.6.0", "@pythnetwork/price-service-sdk@^1.7.1": +"@pythnetwork/price-service-sdk@*", "@pythnetwork/price-service-sdk@1.7.1", "@pythnetwork/price-service-sdk@>=1.6.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@pythnetwork/price-service-sdk/-/price-service-sdk-1.7.1.tgz#dbfc8a8c2189f526346c1f79ec3995e89b690700" integrity sha512-xr2boVXTyv1KUt/c6llUTfbv2jpud99pWlMJbFaHGUBoygQsByuy7WbjIJKZ+0Blg1itLZl0Lp/pJGGg8SdJoQ== dependencies: bn.js "^5.2.1" -"@pythnetwork/pyth-solana-receiver@^0.7.0": +"@pythnetwork/pyth-solana-receiver@0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@pythnetwork/pyth-solana-receiver/-/pyth-solana-receiver-0.7.0.tgz#253a0d15a135d625ceca7ba1b47940dd03b9cab6" integrity sha512-OoEAHh92RPRdKkfjkcKGrjC+t0F3SEL754iKFmixN9zyS8pIfZSVfFntmkHa9pWmqEMxdx/i925a8B5ny8Tuvg== @@ -2294,7 +2266,7 @@ ajv@^8.0.1, ajv@^8.1.0: require-from-string "^2.0.2" uri-js "^4.2.2" -anchor-bankrun@^0.3.0: +anchor-bankrun@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/anchor-bankrun/-/anchor-bankrun-0.3.0.tgz#3789fcecbc201a2334cff228b99cc0da8ef0167e" integrity sha512-PYBW5fWX+iGicIS5MIM/omhk1tQPUc0ELAnI/IkLKQJ6d75De/CQRh8MF2bU/TgGyFi6zEel80wUe3uRol9RrQ== @@ -4076,6 +4048,11 @@ nanoid@3.3.3: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== +nanoid@3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + natural-compare-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" @@ -4094,7 +4071,7 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-cache@^5.1.2: +node-cache@5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== @@ -4815,7 +4792,7 @@ solana-bankrun-linux-x64-musl@0.3.1: resolved "https://registry.yarnpkg.com/solana-bankrun-linux-x64-musl/-/solana-bankrun-linux-x64-musl-0.3.1.tgz#1a044a132138a0084e82406ec7bf4939f06bed68" integrity sha512-6r8i0NuXg3CGURql8ISMIUqhE7Hx/O7MlIworK4oN08jYrP0CXdLeB/hywNn7Z8d1NXrox/NpYUgvRm2yIzAsQ== -solana-bankrun@^0.3.0: +solana-bankrun@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/solana-bankrun/-/solana-bankrun-0.3.1.tgz#13665ab7c1c15ec2b3354aae56980d0ded514998" integrity sha512-inRwON7fBU5lPC36HdEqPeDg15FXJYcf77+o0iz9amvkUMJepcwnRwEfTNyMVpVYdgjTOBW5vg+596/3fi1kGA== @@ -4872,7 +4849,7 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -strict-event-emitter-types@^2.0.0: +strict-event-emitter-types@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz#05e15549cb4da1694478a53543e4e2f4abcf277f" integrity sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA== @@ -5167,7 +5144,7 @@ uuid@8.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== -uuid@^8.3.2: +uuid@8.3.2, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -5359,7 +5336,7 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.7.2: +yargs@17.7.2, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -5382,7 +5359,7 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zstddec@^0.1.0: +zstddec@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.1.0.tgz#7050f3f0e0c3978562d0c566b3e5a427d2bad7ec" integrity sha512-w2NTI8+3l3eeltKAdK8QpiLo/flRAr2p8AGeakfMZOXBxOg9HIu4LVDxBi81sYgVhFhdJjv1OrB5ssI8uFPoLg==