Skip to content

Commit c20bc1f

Browse files
authored
add pyth lazer subscriber (#336)
* add pyth lazer subscriber * update filler to new program type and pyth lazer subscriber
1 parent 75f424d commit c20bc1f

File tree

4 files changed

+191
-159
lines changed

4 files changed

+191
-159
lines changed

src/bots/pythLazerCranker.ts

Lines changed: 24 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ import { PriceUpdateAccount } from '@pythnetwork/pyth-solana-receiver/lib/PythSo
55
import {
66
BlockhashSubscriber,
77
DriftClient,
8-
getOracleClient,
98
getPythLazerOraclePublicKey,
10-
OracleClient,
11-
OracleSource,
129
PriorityFeeSubscriber,
1310
TxSigAndSlot,
1411
} from '@drift-labs/sdk';
@@ -19,7 +16,7 @@ import {
1916
} from '@solana/web3.js';
2017
import { chunks, simulateAndGetTxWithCUs, sleepMs } from '../utils';
2118
import { Agent, setGlobalDispatcher } from 'undici';
22-
import { PythLazerClient } from '@pythnetwork/pyth-lazer-sdk';
19+
import { PythLazerSubscriber } from '../pythLazerSubscriber';
2320

2421
setGlobalDispatcher(
2522
new Agent({
@@ -30,20 +27,16 @@ setGlobalDispatcher(
3027
const SIM_CU_ESTIMATE_MULTIPLIER = 1.5;
3128

3229
export class PythLazerCrankerBot implements Bot {
33-
private wsClient: PythLazerClient;
34-
private pythOracleClient: OracleClient;
30+
private pythLazerClient: PythLazerSubscriber;
3531
readonly decodeFunc: (name: string, data: Buffer) => PriceUpdateAccount;
3632

3733
public name: string;
3834
public dryRun: boolean;
3935
private intervalMs: number;
40-
private feedIdChunkToPriceMessage: Map<number[], string> = new Map();
4136
public defaultIntervalMs = 30_000;
4237

4338
private blockhashSubscriber: BlockhashSubscriber;
4439
private health: boolean = true;
45-
private slotStalenessThresholdRestart: number = 300;
46-
private txSuccessRateThreshold: number = 0.5;
4740

4841
constructor(
4942
private globalConfig: GlobalConfig,
@@ -64,16 +57,20 @@ export class PythLazerCrankerBot implements Bot {
6457
throw new Error('Only devnet drift env is supported');
6558
}
6659

67-
const hermesEndpointParts = globalConfig.hermesEndpoint.split('?token=');
68-
this.wsClient = new PythLazerClient(
69-
hermesEndpointParts[0],
70-
hermesEndpointParts[1]
60+
const updateConfigs = this.crankConfigs.updateConfigs;
61+
const feedIdChunks = chunks(Object.keys(updateConfigs), 11).map((chunk) =>
62+
chunk.map((alias) => {
63+
return updateConfigs[alias].feedId;
64+
})
7165
);
7266

73-
this.pythOracleClient = getOracleClient(
74-
OracleSource.PYTH_LAZER,
75-
driftClient.connection,
76-
driftClient.program
67+
if (!this.globalConfig.lazerEndpoint || !this.globalConfig.lazerToken) {
68+
throw new Error('Missing lazerEndpoint or lazerToken in global config');
69+
}
70+
this.pythLazerClient = new PythLazerSubscriber(
71+
this.globalConfig.lazerEndpoint,
72+
this.globalConfig.lazerToken,
73+
feedIdChunks
7774
);
7875
this.decodeFunc =
7976
this.driftClient.program.account.pythLazerOracle.coder.accounts.decodeUnchecked.bind(
@@ -83,9 +80,6 @@ export class PythLazerCrankerBot implements Bot {
8380
this.blockhashSubscriber = new BlockhashSubscriber({
8481
connection: driftClient.connection,
8582
});
86-
this.txSuccessRateThreshold = crankConfigs.txSuccessRateThreshold;
87-
this.slotStalenessThresholdRestart =
88-
crankConfigs.slotStalenessThresholdRestart;
8983
}
9084

9185
async init(): Promise<void> {
@@ -95,70 +89,23 @@ export class PythLazerCrankerBot implements Bot {
9589
await this.driftClient.fetchMarketLookupTableAccount()
9690
);
9791

98-
const updateConfigs = this.crankConfigs.updateConfigs;
99-
100-
let subscriptionId = 1;
101-
for (const configChunk of chunks(Object.keys(updateConfigs), 11)) {
102-
const priceFeedIds: number[] = configChunk.map((alias) => {
103-
return updateConfigs[alias].feedId;
104-
});
105-
106-
const sendMessage = () =>
107-
this.wsClient.send({
108-
type: 'subscribe',
109-
subscriptionId,
110-
priceFeedIds,
111-
properties: ['price'],
112-
chains: ['solana'],
113-
deliveryFormat: 'json',
114-
channel: 'fixed_rate@200ms',
115-
jsonBinaryEncoding: 'hex',
116-
});
117-
if (this.wsClient.ws.readyState != 1) {
118-
this.wsClient.ws.addEventListener('open', () => {
119-
sendMessage();
120-
});
121-
} else {
122-
sendMessage();
123-
}
124-
125-
this.wsClient.addMessageListener((message) => {
126-
switch (message.type) {
127-
case 'json': {
128-
if (message.value.type == 'streamUpdated') {
129-
if (message.value.solana?.data)
130-
this.feedIdChunkToPriceMessage.set(
131-
priceFeedIds,
132-
message.value.solana.data
133-
);
134-
}
135-
break;
136-
}
137-
default: {
138-
break;
139-
}
140-
}
141-
});
142-
subscriptionId++;
143-
}
92+
await this.pythLazerClient.subscribe();
14493

14594
this.priorityFeeSubscriber?.updateAddresses(
146-
Object.keys(this.feedIdChunkToPriceMessage)
147-
.flat()
148-
.map((feedId) =>
149-
getPythLazerOraclePublicKey(
150-
this.driftClient.program.programId,
151-
Number(feedId)
152-
)
95+
this.pythLazerClient.allSubscribedIds.map((feedId) =>
96+
getPythLazerOraclePublicKey(
97+
this.driftClient.program.programId,
98+
Number(feedId)
15399
)
100+
)
154101
);
155102
}
156103

157104
async reset(): Promise<void> {
158105
logger.info(`Resetting ${this.name} bot`);
159106
this.blockhashSubscriber.unsubscribe();
160107
await this.driftClient.unsubscribe();
161-
this.wsClient.ws.close();
108+
this.pythLazerClient.unsubscribe();
162109
}
163110

164111
async startIntervalLoop(intervalMs = this.intervalMs): Promise<void> {
@@ -187,9 +134,10 @@ export class PythLazerCrankerBot implements Bot {
187134

188135
async runCrankLoop() {
189136
for (const [
190-
feedIds,
137+
feedIdsStr,
191138
priceMessage,
192-
] of this.feedIdChunkToPriceMessage.entries()) {
139+
] of this.pythLazerClient.feedIdChunkToPriceMessage.entries()) {
140+
const feedIds = this.pythLazerClient.getPriceFeedIdsFromHash(feedIdsStr);
193141
const ixs = [
194142
ComputeBudgetProgram.setComputeUnitLimit({
195143
units: 1_400_000,

src/experimental-bots/entrypoint.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import { PythPriceFeedSubscriber } from '../pythPriceFeedSubscriber';
4040
import { SwiftMaker } from './swift/makerExample';
4141
import { SwiftTaker } from './swift/takerExample';
4242
import * as net from 'net';
43-
import { PythLazerClient } from '@pythnetwork/pyth-lazer-sdk';
4443

4544
setGlobalDispatcher(
4645
new Agent({
@@ -308,20 +307,6 @@ const runBot = async () => {
308307
if (!config.botConfigs?.fillerMultithreaded) {
309308
throw new Error('fillerMultithreaded bot config not found');
310309
}
311-
312-
let pythLazerClient: PythLazerClient | undefined;
313-
if (config.global.driftEnv! === 'devnet') {
314-
if (!config.global.lazerEndpoint || !config.global.lazerToken) {
315-
throw new Error(
316-
'Must set environment variables LAZER_ENDPOINT and LAZER_TOKEN'
317-
);
318-
}
319-
pythLazerClient = new PythLazerClient(
320-
config.global.lazerEndpoint,
321-
config.global.lazerToken
322-
);
323-
}
324-
325310
// Ensure that there are no duplicate market indexes in the Array<number[]> marketIndexes config
326311
const marketIndexes = new Set<number>();
327312
for (const marketIndexList of config.botConfigs.fillerMultithreaded
@@ -350,7 +335,6 @@ const runBot = async () => {
350335
},
351336
bundleSender,
352337
pythPriceSubscriber,
353-
pythLazerClient,
354338
[]
355339
);
356340
bots.push(fillerMultithreaded);

0 commit comments

Comments
 (0)