Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

master -> mainnet #349

Merged
merged 11 commits into from
Jan 18, 2025
Merged
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"main": "lib/index.js",
"license": "Apache-2.0",
"dependencies": {
"@drift-labs/jit-proxy": "0.12.47",
"@drift-labs/sdk": "2.107.0-beta.3",
"@drift-labs/jit-proxy": "0.12.52",
"@drift-labs/sdk": "2.107.0-beta.8",
"@opentelemetry/api": "1.7.0",
"@opentelemetry/auto-instrumentations-node": "0.31.2",
"@opentelemetry/exporter-prometheus": "0.31.0",
Expand Down
35 changes: 31 additions & 4 deletions src/bots/filler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
QUOTE_PRECISION,
ClockSubscriber,
DriftEnv,
PerpMarkets,
} from '@drift-labs/sdk';
import { Mutex, tryAcquire, E_ALREADY_LOCKED } from 'async-mutex';

Expand Down Expand Up @@ -75,6 +76,7 @@ import {
import { getErrorCode } from '../error';
import {
SimulateAndGetTxWithCUsResponse,
chunks,
getAllPythOracleUpdateIxs,
getFillSignatureFromUserAccountAndOrderId,
getNodeToFillSignature,
Expand All @@ -95,6 +97,7 @@ import { LRUCache } from 'lru-cache';
import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes';
import { PythPriceFeedSubscriber } from '../pythPriceFeedSubscriber';
import { TxThreaded } from './common/txThreaded';
import { PythLazerSubscriber } from '../pythLazerSubscriber';

const TX_COUNT_COOLDOWN_ON_BURST = 10; // send this many tx before resetting burst mode
const FILL_ORDER_THROTTLE_BACKOFF = 1000; // the time to wait before trying to fill a throttled (error filling) node again
Expand Down Expand Up @@ -235,6 +238,7 @@ export class FillerBot extends TxThreaded implements Bot {
protected rebalanceSettledPnlThreshold: BN;

pythPriceSubscriber?: PythPriceFeedSubscriber;
pythLazerSubscriber?: PythLazerSubscriber;

constructor(
slotSubscriber: SlotSubscriber,
Expand Down Expand Up @@ -362,6 +366,25 @@ export class FillerBot extends TxThreaded implements Bot {
});

this.signerPubkey = this.driftClient.wallet.publicKey.toBase58();

// Pyth lazer: remember to remove devnet guard
if (this.globalConfig.driftEnv == 'devnet') {
if (!this.globalConfig.lazerEndpoint || !this.globalConfig.lazerToken) {
throw new Error('Missing lazerEndpoint or lazerToken in global config');
}

const markets = PerpMarkets[this.globalConfig.driftEnv!].filter(
(market) => market.pythLazerId !== undefined
);
const pythLazerIds = markets.map((m) => m.pythLazerId!);
const pythLazerIdsChunks = chunks(pythLazerIds, 5);
this.pythLazerSubscriber = new PythLazerSubscriber(
this.globalConfig.lazerEndpoint,
this.globalConfig.lazerToken,
pythLazerIdsChunks,
this.globalConfig.driftEnv
);
}
}

protected initializeMetrics(metricsPort?: number) {
Expand Down Expand Up @@ -512,6 +535,7 @@ export class FillerBot extends TxThreaded implements Bot {
);

await this.clockSubscriber.subscribe();
await this.pythLazerSubscriber?.subscribe();

this.lutAccounts.push(
await this.driftClient.fetchMarketLookupTableAccount()
Expand Down Expand Up @@ -1382,7 +1406,8 @@ export class FillerBot extends TxThreaded implements Bot {
}

private async getPythIxsFromNode(
node: NodeToFill | NodeToTrigger
node: NodeToFill | NodeToTrigger,
precedingIxs: TransactionInstruction[] = []
): Promise<TransactionInstruction[]> {
const marketIndex = node.node.order?.marketIndex;
if (marketIndex === undefined) {
Expand Down Expand Up @@ -1411,7 +1436,8 @@ export class FillerBot extends TxThreaded implements Bot {
MarketType.PERP,
this.pythPriceSubscriber!,
this.driftClient,
this.globalConfig.numNonActiveOraclesToPush ?? 0
this.pythLazerSubscriber,
precedingIxs
);
return pythIxs;
}
Expand Down Expand Up @@ -1703,9 +1729,10 @@ export class FillerBot extends TxThreaded implements Bot {
let removeLastIxPostSim = this.revertOnFailure;
if (
this.pythPriceSubscriber &&
this.pythLazerSubscriber &&
((makerInfos.length == 2 && !referrerInfo) || makerInfos.length < 2)
) {
const pythIxs = await this.getPythIxsFromNode(nodeToFill);
const pythIxs = await this.getPythIxsFromNode(nodeToFill, ixs);
ixs.push(...pythIxs);
removeLastIxPostSim = false;
}
Expand Down Expand Up @@ -1930,7 +1957,7 @@ export class FillerBot extends TxThreaded implements Bot {

let removeLastIxPostSim = this.revertOnFailure;
if (this.pythPriceSubscriber) {
const pythIxs = await this.getPythIxsFromNode(nodeToTrigger);
const pythIxs = await this.getPythIxsFromNode(nodeToTrigger, ixs);
ixs.push(...pythIxs);
removeLastIxPostSim = false;
}
Expand Down
41 changes: 37 additions & 4 deletions src/bots/makerBidAskTwapCrank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
DriftMarketInfo,
isOneOfVariant,
getVariant,
PerpMarkets,
} from '@drift-labs/sdk';
import { Mutex } from 'async-mutex';

Expand All @@ -31,13 +32,15 @@ import {
import { webhookMessage } from '../webhook';
import { ConfirmOptions, Signer } from '@solana/web3.js';
import {
chunks,
getAllPythOracleUpdateIxs,
getDriftPriorityFeeEndpoint,
handleSimResultError,
simulateAndGetTxWithCUs,
SimulateAndGetTxWithCUsResponse,
} from '../utils';
import { PythPriceFeedSubscriber } from '../pythPriceFeedSubscriber';
import { PythLazerSubscriber } from '../pythLazerSubscriber';
import { PythPullClient } from '@drift-labs/sdk';
import { BundleSender } from '../bundleSender';

Expand Down Expand Up @@ -176,6 +179,7 @@ export class MakerBidAskTwapCrank implements Bot {
private watchdogTimerMutex = new Mutex();
private watchdogTimerLastPatTime = Date.now();
private pythPriceSubscriber?: PythPriceFeedSubscriber;
private pythLazerSubscriber?: PythLazerSubscriber;
private pythPullOracleClient: PythPullClient;
private pythHealthy: boolean = true;
private lookupTableAccounts: AddressLookupTableAccount[];
Expand Down Expand Up @@ -204,9 +208,30 @@ export class MakerBidAskTwapCrank implements Bot {
this.lookupTableAccounts = lookupTableAccounts;
this.pythPullOracleClient = new PythPullClient(this.driftClient.connection);
this.bundleSender = bundleSender;

// Pyth lazer: remember to remove devnet guard
if (this.globalConfig.driftEnv == 'devnet') {
if (!this.globalConfig.lazerEndpoint || !this.globalConfig.lazerToken) {
throw new Error('Missing lazerEndpoint or lazerToken in global config');
}

const markets = PerpMarkets[this.globalConfig.driftEnv!].filter(
(market) => market.pythLazerId !== undefined
);
const pythLazerIds = markets.map((m) => m.pythLazerId!);
const pythLazerIdsChunks = chunks(pythLazerIds, 10);
this.pythLazerSubscriber = new PythLazerSubscriber(
this.globalConfig.lazerEndpoint,
this.globalConfig.lazerToken,
pythLazerIdsChunks,
this.globalConfig.driftEnv
);
}
}

public async init() {
await this.pythLazerSubscriber?.subscribe();

logger.info(`[${this.name}] initing, runOnce: ${this.runOnce}`);
this.lookupTableAccounts.push(
await this.driftClient.fetchMarketLookupTableAccount()
Expand Down Expand Up @@ -403,7 +428,8 @@ export class MakerBidAskTwapCrank implements Bot {
}

private async getPythIxsFromTwapCrankInfo(
crankMarketIndex: number
crankMarketIndex: number,
precedingIxs: TransactionInstruction[] = []
): Promise<TransactionInstruction[]> {
if (crankMarketIndex === undefined) {
throw new Error('Market index not found on node');
Expand All @@ -417,7 +443,8 @@ export class MakerBidAskTwapCrank implements Bot {
MarketType.PERP,
this.pythPriceSubscriber!,
this.driftClient,
this.globalConfig.numNonActiveOraclesToPush ?? 0
this.pythLazerSubscriber,
precedingIxs
);
return pythIxs;
}
Expand Down Expand Up @@ -548,10 +575,16 @@ export class MakerBidAskTwapCrank implements Bot {
this.pythPriceSubscriber &&
isOneOfVariant(
this.driftClient.getPerpMarketAccount(mi)!.amm.oracleSource,
['pythPull', 'pyth1KPull', 'pyth1MPull', 'pythStableCoinPull']
[
'pythPull',
'pyth1KPull',
'pyth1MPull',
'pythStableCoinPull',
'pythLazer',
]
)
) {
const pythIxs = await this.getPythIxsFromTwapCrankInfo(mi);
const pythIxs = await this.getPythIxsFromTwapCrankInfo(mi, ixs);
ixs.push(...pythIxs);
pythIxsPushed = true;
} else if (usingSwitchboardOnDemand) {
Expand Down
25 changes: 19 additions & 6 deletions src/bots/pythLazerCranker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import { GlobalConfig, PythLazerCrankerBotConfig } from '../config';
import { PriceUpdateAccount } from '@pythnetwork/pyth-solana-receiver/lib/PythSolanaReceiver';
import {
BlockhashSubscriber,
DevnetPerpMarkets,
DevnetSpotMarkets,
DriftClient,
getPythLazerOraclePublicKey,
MainnetPerpMarkets,
MainnetSpotMarkets,
PriorityFeeSubscriber,
TxSigAndSlot,
} from '@drift-labs/sdk';
Expand Down Expand Up @@ -57,12 +61,21 @@ export class PythLazerCrankerBot implements Bot {
throw new Error('Only devnet drift env is supported');
}

const updateConfigs = this.crankConfigs.updateConfigs;
const feedIdChunks = chunks(Object.keys(updateConfigs), 11).map((chunk) =>
chunk.map((alias) => {
return updateConfigs[alias].feedId;
})
);
const spotMarkets =
this.globalConfig.driftEnv === 'mainnet-beta'
? DevnetSpotMarkets
: MainnetSpotMarkets;
const perpMarkets =
this.globalConfig.driftEnv === 'mainnet-beta'
? DevnetPerpMarkets
: MainnetPerpMarkets;

const allFeedIds = [
...spotMarkets.map((market) => market.pythLazerId),
...perpMarkets.map((market) => market.pythLazerId),
].filter((id) => id !== undefined) as number[];
const allFeedIdsSet = new Set(allFeedIds);
const feedIdChunks = chunks(Array.from(allFeedIdsSet), 11);

if (!this.globalConfig.lazerEndpoint || !this.globalConfig.lazerToken) {
throw new Error('Missing lazerEndpoint or lazerToken in global config');
Expand Down
27 changes: 0 additions & 27 deletions src/bots/spotFiller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
DriftEnv,
DriftClient,
SpotMarketAccount,
MakerInfo,
Expand Down Expand Up @@ -69,7 +68,6 @@ import {
} from './common/txLogParse';
import { FillerConfig, GlobalConfig } from '../config';
import {
getAllPythOracleUpdateIxs,
getFillSignatureFromUserAccountAndOrderId,
getNodeToFillSignature,
getNodeToTriggerSignature,
Expand Down Expand Up @@ -97,7 +95,6 @@ import {
import { selectMakers } from '../makerSelection';
import { LRUCache } from 'lru-cache';
import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes';
import { PythPriceFeedSubscriber } from '../pythPriceFeedSubscriber';
import { FallbackLiquiditySource } from '../experimental-bots/filler-common/types';

const THROTTLED_NODE_SIZE_TO_PRUNE = 10; // Size of throttled nodes to get to before pruning the map
Expand Down Expand Up @@ -319,7 +316,6 @@ export class SpotFillerBot implements Bot {
protected minGasBalanceToFill: number;
protected rebalanceSettledPnlThreshold: BN;

protected pythPriceSubscriber?: PythPriceFeedSubscriber;
protected lookupTableAccounts: AddressLookupTableAccount[];

constructor(
Expand All @@ -331,7 +327,6 @@ export class SpotFillerBot implements Bot {
priorityFeeSubscriber: PriorityFeeSubscriber,
blockhashSubscriber: BlockhashSubscriber,
bundleSender?: BundleSender,
pythPriceSubscriber?: PythPriceFeedSubscriber,
lookupTableAccounts: AddressLookupTableAccount[] = []
) {
this.globalConfig = globalConfig;
Expand Down Expand Up @@ -389,7 +384,6 @@ export class SpotFillerBot implements Bot {
`${this.name}: rebalancing enabled: ${this.jupiterClient !== undefined}`
);

this.pythPriceSubscriber = pythPriceSubscriber;
this.lookupTableAccounts = lookupTableAccounts;

this.userMap = userMap;
Expand Down Expand Up @@ -1707,27 +1701,6 @@ export class SpotFillerBot implements Bot {
}
}

private async getPythIxsFromNode(
node: NodeToFill | NodeToTrigger
): Promise<TransactionInstruction[]> {
const marketIndex = node.node.order?.marketIndex;
if (marketIndex === undefined) {
throw new Error('Market index not found on node');
}
if (!this.pythPriceSubscriber) {
throw new Error('Pyth price subscriber not initialized');
}
const pythIxs = await getAllPythOracleUpdateIxs(
this.runtimeSpec.driftEnv as DriftEnv,
marketIndex,
MarketType.SPOT,
this.pythPriceSubscriber!,
this.driftClient,
this.globalConfig.numNonActiveOraclesToPush ?? 0
);
return pythIxs;
}

/**
*
* @param fillTxId id of current fill
Expand Down
2 changes: 0 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ export interface GlobalConfig {
hermesEndpoint?: string;
lazerEndpoint?: string;
lazerToken?: string;
numNonActiveOraclesToPush?: number;

// Optional to specify markets loaded by drift client
perpMarketsToLoad?: Array<number>;
Expand Down Expand Up @@ -247,7 +246,6 @@ const defaultConfig: Partial<Config> = {

endpoint: process.env.ENDPOINT!,
hermesEndpoint: process.env.HERMES_ENDPOINT,
numNonActiveOraclesToPush: 0,
wsEndpoint: process.env.WS_ENDPOINT,
heliusEndpoint: process.env.HELIUS_ENDPOINT,
additionalSendTxEndpoints: [],
Expand Down
4 changes: 2 additions & 2 deletions src/experimental-bots/filler-common/dlobBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class DLOBBuilder {
) {
return;
}
this.dlob.insertOrder(
dlob.insertOrder(
order,
pubkey,
this.slotSubscriber.getSlot(),
Expand All @@ -192,7 +192,7 @@ class DLOBBuilder {
slot,
}: SwiftOrderParamsMessage = this.driftClient.program.coder.types.decode(
'SwiftOrderParamsMessage',
Buffer.from(orderData['order_message'], 'base64')
Buffer.from(orderData['order_message'], 'hex')
);

const takerAuthority = new PublicKey(orderData['taker_authority']);
Expand Down
Loading