-
Notifications
You must be signed in to change notification settings - Fork 100
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
Add phoenix subscriber #444
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { Connection, PublicKey } from '@solana/web3.js'; | ||
import { PhoenixSubscriber } from '../src'; | ||
import { PROGRAM_ID } from '@ellipsis-labs/phoenix-sdk'; | ||
|
||
export async function listenToBook(): Promise<void> { | ||
const connection = new Connection('https://api.mainnet-beta.solana.com'); | ||
|
||
const phoenixSubscriber = new PhoenixSubscriber({ | ||
connection, | ||
programId: PROGRAM_ID, | ||
marketAddress: new PublicKey( | ||
'4DoNfFBfF7UokCC2FQzriy7yHK6DY6NVdYpuekQ5pRgg' | ||
), | ||
accountSubscription: { | ||
type: 'websocket', | ||
}, | ||
}); | ||
|
||
await phoenixSubscriber.subscribe(); | ||
|
||
for (let i = 0; i < 10; i++) { | ||
const bid = phoenixSubscriber.getBestBid(); | ||
const ask = phoenixSubscriber.getBestAsk(); | ||
console.log(`iter ${i}:`, bid.toFixed(3), '@', ask.toFixed(3)); | ||
await new Promise((r) => setTimeout(r, 2000)); | ||
} | ||
|
||
await phoenixSubscriber.unsubscribe(); | ||
} | ||
|
||
(async function () { | ||
try { | ||
await listenToBook(); | ||
} catch (err) { | ||
console.log('Error: ', err); | ||
process.exit(1); | ||
} | ||
|
||
process.exit(0); | ||
})(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { PublicKey } from '@solana/web3.js'; | ||
import { PhoenixV1FulfillmentConfigAccount } from '../types'; | ||
import { DriftClient } from '../driftClient'; | ||
|
||
export class PhoenixFulfillmentConfigMap { | ||
driftClient: DriftClient; | ||
map = new Map<number, PhoenixV1FulfillmentConfigAccount>(); | ||
|
||
public constructor(driftClient: DriftClient) { | ||
this.driftClient = driftClient; | ||
} | ||
|
||
public async add( | ||
marketIndex: number, | ||
phoenixMarketAddress: PublicKey | ||
): Promise<void> { | ||
const account = await this.driftClient.getPhoenixV1FulfillmentConfig( | ||
phoenixMarketAddress | ||
); | ||
this.map.set(marketIndex, account); | ||
} | ||
|
||
public get(marketIndex: number): PhoenixV1FulfillmentConfigAccount { | ||
return this.map.get(marketIndex); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,154 @@ | ||||
import { Connection, PublicKey, SYSVAR_CLOCK_PUBKEY } from '@solana/web3.js'; | ||||
import { BulkAccountLoader } from '../accounts/bulkAccountLoader'; | ||||
import { | ||||
MarketData, | ||||
Client, | ||||
deserializeMarketData, | ||||
deserializeClockData, | ||||
toNum, | ||||
getMarketUiLadder, | ||||
} from '@ellipsis-labs/phoenix-sdk'; | ||||
|
||||
export type PhoenixMarketSubscriberConfig = { | ||||
connection: Connection; | ||||
programId: PublicKey; | ||||
marketAddress: PublicKey; | ||||
accountSubscription: | ||||
| { | ||||
// enables use to add web sockets in the future | ||||
type: 'polling'; | ||||
accountLoader: BulkAccountLoader; | ||||
} | ||||
| { | ||||
type: 'websocket'; | ||||
}; | ||||
}; | ||||
|
||||
export class PhoenixSubscriber { | ||||
connection: Connection; | ||||
client: Client; | ||||
programId: PublicKey; | ||||
marketAddress: PublicKey; | ||||
subscriptionType: 'polling' | 'websocket'; | ||||
accountLoader: BulkAccountLoader | undefined; | ||||
market: MarketData; | ||||
marketCallbackId: string | number; | ||||
clockCallbackId: string | number; | ||||
|
||||
subscribed: boolean; | ||||
lastSlot: number; | ||||
lastUnixTimestamp: number; | ||||
|
||||
public constructor(config: PhoenixMarketSubscriberConfig) { | ||||
this.connection = config.connection; | ||||
this.programId = config.programId; | ||||
this.marketAddress = config.marketAddress; | ||||
if (config.accountSubscription.type === 'polling') { | ||||
this.subscriptionType = 'polling'; | ||||
this.accountLoader = config.accountSubscription.accountLoader; | ||||
} else { | ||||
this.subscriptionType = 'websocket'; | ||||
} | ||||
this.lastSlot = 0; | ||||
this.lastUnixTimestamp = 0; | ||||
} | ||||
|
||||
public async subscribe(): Promise<void> { | ||||
if (this.subscribed) { | ||||
return; | ||||
} | ||||
|
||||
this.market = deserializeMarketData( | ||||
(await this.connection.getAccountInfo(this.marketAddress, 'confirmed')) | ||||
.data | ||||
); | ||||
|
||||
const clock = deserializeClockData( | ||||
(await this.connection.getAccountInfo(SYSVAR_CLOCK_PUBKEY, 'confirmed')) | ||||
.data | ||||
); | ||||
this.lastUnixTimestamp = toNum(clock.unixTimestamp); | ||||
|
||||
if (this.subscriptionType === 'websocket') { | ||||
this.marketCallbackId = this.connection.onAccountChange( | ||||
this.marketAddress, | ||||
(accountInfo, _ctx) => { | ||||
this.market = deserializeMarketData(accountInfo.data); | ||||
} | ||||
); | ||||
this.clockCallbackId = this.connection.onAccountChange( | ||||
SYSVAR_CLOCK_PUBKEY, | ||||
(accountInfo, ctx) => { | ||||
this.lastSlot = ctx.slot; | ||||
const clock = deserializeClockData(accountInfo.data); | ||||
this.lastUnixTimestamp = toNum(clock.unixTimestamp); | ||||
} | ||||
); | ||||
} else { | ||||
this.marketCallbackId = await this.accountLoader.addAccount( | ||||
this.marketAddress, | ||||
(buffer, slot) => { | ||||
this.marketCallbackId = slot; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. think you meant to update |
||||
this.market = deserializeMarketData(buffer); | ||||
} | ||||
); | ||||
this.clockCallbackId = await this.accountLoader.addAccount( | ||||
SYSVAR_CLOCK_PUBKEY, | ||||
(buffer, slot) => { | ||||
this.clockCallbackId = slot; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. think you meant to update |
||||
const clock = deserializeClockData(buffer); | ||||
this.lastUnixTimestamp = toNum(clock.unixTimestamp); | ||||
} | ||||
); | ||||
} | ||||
|
||||
this.subscribed = true; | ||||
} | ||||
|
||||
public getBestBid(): number { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm having this return the price in human readable units. Lmk if you prefer something different There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this and protocol-v2/sdk/src/serum/serumSubscriber.ts Line 112 in 006b269
|
||||
const ladder = getMarketUiLadder( | ||||
this.market, | ||||
this.lastSlot, | ||||
this.lastUnixTimestamp, | ||||
1 | ||||
); | ||||
return ladder.bids[0][0]; | ||||
} | ||||
|
||||
public getBestAsk(): number { | ||||
const ladder = getMarketUiLadder( | ||||
this.market, | ||||
this.lastSlot, | ||||
this.lastUnixTimestamp, | ||||
1 | ||||
); | ||||
return ladder.asks[0][0]; | ||||
} | ||||
|
||||
public async unsubscribe(): Promise<void> { | ||||
if (!this.subscribed) { | ||||
return; | ||||
} | ||||
|
||||
// remove listeners | ||||
if (this.subscriptionType === 'websocket') { | ||||
await this.connection.removeAccountChangeListener( | ||||
this.marketCallbackId as number | ||||
); | ||||
await this.connection.removeAccountChangeListener( | ||||
this.clockCallbackId as number | ||||
); | ||||
} else { | ||||
this.accountLoader.removeAccount( | ||||
this.marketAddress, | ||||
this.marketCallbackId as string | ||||
); | ||||
this.accountLoader.removeAccount( | ||||
SYSVAR_CLOCK_PUBKEY, | ||||
this.clockCallbackId as string | ||||
); | ||||
} | ||||
|
||||
this.subscribed = false; | ||||
} | ||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lmk if you prefer 2.25.1