@@ -5,10 +5,7 @@ import { PriceUpdateAccount } from '@pythnetwork/pyth-solana-receiver/lib/PythSo
55import {
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' ;
2017import { chunks , simulateAndGetTxWithCUs , sleepMs } from '../utils' ;
2118import { Agent , setGlobalDispatcher } from 'undici' ;
22- import { PythLazerClient } from '@pythnetwork/pyth-lazer-sdk ' ;
19+ import { PythLazerSubscriber } from '../pythLazerSubscriber ' ;
2320
2421setGlobalDispatcher (
2522 new Agent ( {
@@ -30,20 +27,16 @@ setGlobalDispatcher(
3027const SIM_CU_ESTIMATE_MULTIPLIER = 1.5 ;
3128
3229export 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 ,
0 commit comments