diff --git a/src/bots/pythCranker.ts b/src/bots/pythCranker.ts index 2125224d..2f04e17b 100644 --- a/src/bots/pythCranker.ts +++ b/src/bots/pythCranker.ts @@ -128,7 +128,20 @@ export class PythCrankerBot implements Bot { await this.driftClient.fetchMarketLookupTableAccount() ); - for (const marketConfig of PerpMarkets[this.globalConfig.driftEnv]) { + const perpMarketIndexes = this.driftClient + .getPerpMarketAccounts() + .map((market) => market.marketIndex); + const perpMarketConfigs = PerpMarkets[this.globalConfig.driftEnv].filter( + (market) => perpMarketIndexes.includes(market.marketIndex) + ); + const spotMarketIndexes = this.driftClient + .getSpotMarketAccounts() + .map((market) => market.marketIndex); + const spotMarketConfigs = SpotMarkets[this.globalConfig.driftEnv].filter( + (market) => spotMarketIndexes.includes(market.marketIndex) + ); + + for (const marketConfig of perpMarketConfigs) { const feedId = marketConfig.pythFeedId; if (!feedId) { logger.warn(`No pyth feed id for market ${marketConfig.symbol}`); @@ -165,7 +178,7 @@ export class PythCrankerBot implements Bot { }); } - for (const marketConfig of SpotMarkets[this.globalConfig.driftEnv]) { + for (const marketConfig of spotMarketConfigs) { if ( this.feedIdsToCrank.findIndex( (feedId) => feedId.baseSymbol === marketConfig.symbol diff --git a/src/config.ts b/src/config.ts index 37bca848..91710897 100644 --- a/src/config.ts +++ b/src/config.ts @@ -3,6 +3,7 @@ import YAML from 'yaml'; import { loadCommaDelimitToArray, loadCommaDelimitToStringArray, + parsePositiveIntArray, } from './utils'; import { OrderExecutionAlgoType } from './types'; import { @@ -21,17 +22,6 @@ export type BaseBotConfig = { runOnce?: boolean; }; -export type JitMakerConfig = BaseBotConfig & { - subaccounts?: Array; - marketType: string; - /// @deprecated, use {@link JitMakerConfig.marketIndexes} and {@link JitMakerConfig.marketType} - perpMarketIndicies?: Array; - marketIndexes?: Array; - targetLeverage?: number; - aggressivenessBps?: number; - jitCULimit?: number; -}; - export type UserPnlSettlerConfig = BaseBotConfig & { /// perp market indexes to filter for settling pnl perpMarketIndicies?: Array; @@ -137,14 +127,12 @@ export type BotConfigMap = { trigger?: BaseBotConfig; liquidator?: LiquidatorConfig; floatingMaker?: BaseBotConfig; - jitMaker?: JitMakerConfig; ifRevenueSettler?: BaseBotConfig; fundingRateUpdater?: BaseBotConfig; userPnlSettler?: UserPnlSettlerConfig; userLpSettler?: BaseBotConfig; userIdleFlipper?: BaseBotConfig; markTwapCrank?: BaseBotConfig; - uncrossArb?: BaseBotConfig; pythCranker?: PythCrankerBotConfig; switchboardCranker?: SwitchboardCrankerBotConfig; swiftTaker?: BaseBotConfig; @@ -160,6 +148,10 @@ export interface GlobalConfig { hermesEndpoint?: string; numNonActiveOraclesToPush?: number; + // Optional to specify markets loaded by drift client + perpMarketsToLoad?: Array; + spotMarketsToLoad?: Array; + /// helius endpoint to use helius priority fee strategy heliusEndpoint?: string; /// additional rpc endpoints to send transactions to @@ -232,6 +224,9 @@ const defaultConfig: Partial = { debug: false, subaccounts: [0], + perpMarketsToLoad: parsePositiveIntArray(process.env.PERP_MARKETS_TO_LOAD), + spotMarketsToLoad: parsePositiveIntArray(process.env.SPOT_MARKETS_TO_LOAD), + eventSubscriberPollingInterval: 5000, bulkAccountLoaderPollingInterval: 5000, @@ -455,21 +450,6 @@ export function loadConfigFromOpts(opts: any): Config { runOnce: opts.runOnce ?? false, }; } - if (opts.jitMaker) { - config.enabledBots.push('jitMaker'); - config.botConfigs!.jitMaker = { - dryRun: opts.dryRun ?? false, - botId: process.env.BOT_ID ?? 'jitMaker', - metricsPort: 9464, - runOnce: opts.runOnce ?? false, - perpMarketIndicies: loadCommaDelimitToArray(opts.perpMarketIndicies) ?? [ - 0, - ], - subaccounts: loadCommaDelimitToArray(opts.subaccounts) ?? [0], - marketType: opts.marketType ?? 'perp', - targetLeverage: 1, - }; - } if (opts.ifRevenueSettler) { config.enabledBots.push('ifRevenueSettler'); config.botConfigs!.ifRevenueSettler = { @@ -528,17 +508,6 @@ export function loadConfigFromOpts(opts: any): Config { runOnce: opts.runOnce ?? false, }; } - - if (opts.uncrossArb) { - config.enabledBots.push('uncrossArb'); - config.botConfigs!.uncrossArb = { - dryRun: opts.dryRun ?? false, - botId: process.env.BOT_ID ?? 'uncrossArb', - metricsPort: 9464, - runOnce: opts.runOnce ?? false, - }; - } - return mergeDefaults(defaultConfig, config) as Config; } diff --git a/src/index.ts b/src/index.ts index a9992e3b..a6a42d39 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,10 +24,7 @@ import { TokenFaucet, DriftClientSubscriptionConfig, LogProviderConfig, - getMarketsAndOraclesForSubscription, - AuctionSubscriber, FastSingleTxSender, - OracleInfo, UserMap, Wallet, RetryTxSender, @@ -48,7 +45,6 @@ import { constants } from './types'; import { FillerBot } from './bots/filler'; import { SpotFillerBot } from './bots/spotFiller'; import { TriggerBot } from './bots/trigger'; -import { JitMaker } from './bots/jitMaker'; import { LiquidatorBot } from './bots/liquidator'; import { FloatingPerpMakerBot } from './bots/floatingMaker'; import { Bot } from './types'; @@ -62,6 +58,7 @@ import { TOKEN_FAUCET_PROGRAM_ID, getWallet, loadKeypair, + getMarketsAndOracleInfosToLoad, } from './utils'; import { Config, @@ -71,9 +68,7 @@ import { } from './config'; import { FundingRateUpdaterBot } from './bots/fundingRateUpdater'; import { FillerLiteBot } from './bots/fillerLite'; -import { JitProxyClient, JitterShotgun } from '@drift-labs/jit-proxy/lib'; import { MakerBidAskTwapCrank } from './bots/makerBidAskTwapCrank'; -import { UncrossArbBot } from './bots/uncrossArbBot'; import { BundleSender } from './bundleSender'; import { DriftStateWatcher, StateChecks } from './driftStateWatcher'; import { webhookMessage } from './webhook'; @@ -382,16 +377,15 @@ const runBot = async () => { // keeping these arrays undefined will prompt DriftClient to call `findAllMarketAndOracles` // and load all markets and oracle accounts from on-chain - let perpMarketIndexes: number[] | undefined; - let spotMarketIndexes: number[] | undefined; - let oracleInfos: OracleInfo[] | undefined; - if (configHasBot(config, 'jitMaker')) { - ({ perpMarketIndexes, spotMarketIndexes, oracleInfos } = - getMarketsAndOraclesForSubscription(config.global.driftEnv!)); - } const marketLookupTable = config.global?.lutPubkey ? new PublicKey(config.global.lutPubkey) : new PublicKey(configs[config.global.driftEnv!].MARKET_LOOKUP_TABLE); + const marketsAndOracleInfos = getMarketsAndOracleInfosToLoad( + sdkConfig, + config.global.perpMarketsToLoad, + config.global.spotMarketsToLoad + ); + const oracleInfos = marketsAndOracleInfos.oracleInfos; const driftClientConfig = { connection, wallet, @@ -400,9 +394,9 @@ const runBot = async () => { accountSubscription, env: config.global.driftEnv, userStats: true, - perpMarketIndexes, - spotMarketIndexes, - oracleInfos, + perpMarketIndexes: marketsAndOracleInfos.perpMarketIndicies, + spotMarketIndexes: marketsAndOracleInfos.spotMarketIndicies, + oracleInfos: oracleInfos.length > 0 ? oracleInfos : undefined, activeSubAccountId: config.global.subaccounts![0], subAccountIds: config.global.subaccounts ?? [0], txVersion: 0 as TransactionVersion, @@ -696,60 +690,6 @@ const runBot = async () => { ); } - let auctionSubscriber: AuctionSubscriber | undefined = undefined; - let jitter: JitterShotgun | undefined = undefined; - if (configHasBot(config, 'jitMaker')) { - // Subscribe to drift client - - needCheckDriftUser = true; - needForceCollateral = true; - needPriorityFeeSubscriber = true; - - const jitProxyClient = new JitProxyClient({ - driftClient, - programId: new PublicKey(sdkConfig.JIT_PROXY_PROGRAM_ID!), - }); - - auctionSubscriber = new AuctionSubscriber({ - driftClient, - opts: { commitment: stateCommitment }, - }); - await auctionSubscriber.subscribe(); - - jitter = new JitterShotgun({ - auctionSubscriber, - driftClient, - jitProxyClient, - }); - await jitter.subscribe(); - - const txSenderConnection = new Connection(endpoint, { - wsEndpoint: wsEndpoint, - commitment: stateCommitment, - disableRetryOnRateLimit: true, - }); - driftClient.txSender = new FastSingleTxSender({ - connection: txSenderConnection, - wallet, - blockhashRefreshInterval: 1000, - opts: { - preflightCommitment: 'processed', - skipPreflight: false, - commitment: 'processed', - }, - }); - - bots.push( - new JitMaker( - driftClient, - jitter, - config.botConfigs!.jitMaker!, - config.global.driftEnv!, - priorityFeeSubscriber - ) - ); - } - if (configHasBot(config, 'liquidator')) { needCheckDriftUser = true; needUserMapSubscribe = true; @@ -872,33 +812,11 @@ const runBot = async () => { ); } - if (configHasBot(config, 'uncrossArb')) { - needCheckDriftUser = true; - needPriorityFeeSubscriber = true; - const jitProxyClient = new JitProxyClient({ - driftClient, - programId: new PublicKey(sdkConfig.JIT_PROXY_PROGRAM_ID!), - }); - - bots.push( - new UncrossArbBot( - driftClient, - jitProxyClient, - slotSubscriber, - config.botConfigs!.uncrossArb!, - config.global.driftEnv!, - priorityFeeSubscriber - ) - ); - } - // Run subscribe functions once if ( needCheckDriftUser || needForceCollateral || eventSubscriber || - auctionSubscriber || - jitter || needUserMapSubscribe || needPythPriceSubscriber || needDriftStateWatcher @@ -932,34 +850,6 @@ const runBot = async () => { logger.info(`userMap.subscribe took: ${hrEnd[0]}s ${hrEnd[1] / 1e6}ms`); } - logger.info( - `Checking if need auctionSubscriber: ${auctionSubscriber !== undefined}` - ); - if (auctionSubscriber) { - const hrStart = process.hrtime(); - await auctionSubscriber.subscribe(); - const hrEnd = process.hrtime(hrStart); - logger.info( - `auctionSubscriber.subscribe took: ${hrEnd[0]}s ${hrEnd[1] / 1e6}ms` - ); - } - - logger.info(`Checking if need jitter: ${jitter !== undefined}`); - if (jitter) { - const freeCollateral = driftClient - .getUser() - .getFreeCollateral('Maintenance'); - if (freeCollateral.isZero()) { - throw new Error( - `No collateral in account, collateral is required to run JitMakerBot, run with --force-deposit flag to deposit collateral` - ); - } - const hrStart = process.hrtime(); - await jitter.subscribe(); - const hrEnd = process.hrtime(hrStart); - logger.info(`jitter.subscribe took: ${hrEnd[0]}s ${hrEnd[1] / 1e6}ms`); - } - logger.info(`Checking if need pythConnection: ${needPythPriceSubscriber}`); if (needPythPriceSubscriber && pythPriceSubscriber) { const feedIds: string[] = Array.from( diff --git a/src/utils.ts b/src/utils.ts index ebec05b6..ff2ef031 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -42,6 +42,7 @@ import { DRIFT_ORACLE_RECEIVER_ID, OpenbookV2FulfillmentConfigAccount, OpenbookV2Subscriber, + OracleInfo, } from '@drift-labs/sdk'; import { NATIVE_MINT, @@ -131,6 +132,20 @@ export function loadCommaDelimitToArray(str: string): number[] { } } +export function parsePositiveIntArray( + intArray: string | undefined, + separator = ',' +): number[] | undefined { + if (!intArray) { + return undefined; + } + return intArray + .split(separator) + .map((s) => s.trim()) + .map((s) => parseInt(s)) + .filter((n) => !isNaN(n) && n >= 0); +} + export function loadCommaDelimitToStringArray(str: string): string[] { try { return str.split(',').filter((element) => { @@ -1431,3 +1446,86 @@ export async function checkIfAccountExists( return false; } } + +export function getMarketsAndOracleInfosToLoad( + sdkConfig: any, + perpMarketIndicies: number[] | undefined, + spotMarketIndicies: number[] | undefined +): { + oracleInfos: OracleInfo[]; + perpMarketIndicies: number[] | undefined; + spotMarketIndicies: number[] | undefined; +} { + const oracleInfos: OracleInfo[] = []; + const oraclesTracked = new Set(); + + // only watch all markets if neither env vars are specified + const noMarketsSpecified = !perpMarketIndicies && !spotMarketIndicies; + + let perpIndexes = perpMarketIndicies; + if (!perpIndexes) { + if (noMarketsSpecified) { + perpIndexes = sdkConfig.PERP_MARKETS.map( + (m: PerpMarketConfig) => m.marketIndex + ); + } else { + perpIndexes = []; + } + } + let spotIndexes = spotMarketIndicies; + if (!spotIndexes) { + if (noMarketsSpecified) { + spotIndexes = sdkConfig.SPOT_MARKETS.map( + (m: SpotMarketConfig) => m.marketIndex + ); + } else { + spotIndexes = []; + } + } + + if (perpIndexes && perpIndexes.length > 0) { + for (const idx of perpIndexes) { + const perpMarketConfig = sdkConfig.PERP_MARKETS[idx] as PerpMarketConfig; + if (!perpMarketConfig) { + throw new Error(`Perp market config for ${idx} not found`); + } + const oracleKey = perpMarketConfig.oracle.toBase58(); + if (!oraclesTracked.has(oracleKey)) { + logger.info(`Tracking oracle ${oracleKey} for perp market ${idx}`); + oracleInfos.push({ + publicKey: perpMarketConfig.oracle, + source: perpMarketConfig.oracleSource, + }); + oraclesTracked.add(oracleKey); + } + } + logger.info(`Bot tracking perp markets: ${JSON.stringify(perpIndexes)}`); + } + + if (spotIndexes && spotIndexes.length > 0) { + for (const idx of spotIndexes) { + const spotMarketConfig = sdkConfig.SPOT_MARKETS[idx] as SpotMarketConfig; + if (!spotMarketConfig) { + throw new Error(`Spot market config for ${idx} not found`); + } + const oracleKey = spotMarketConfig.oracle.toBase58(); + if (!oraclesTracked.has(oracleKey)) { + logger.info(`Tracking oracle ${oracleKey} for spot market ${idx}`); + oracleInfos.push({ + publicKey: spotMarketConfig.oracle, + source: spotMarketConfig.oracleSource, + }); + oraclesTracked.add(oracleKey); + } + } + logger.info(`Bot tracking spot markets: ${JSON.stringify(spotIndexes)}`); + } + + return { + oracleInfos, + perpMarketIndicies: + perpIndexes && perpIndexes.length > 0 ? perpIndexes : undefined, + spotMarketIndicies: + spotIndexes && spotIndexes.length > 0 ? spotIndexes : undefined, + }; +}