Skip to content

Commit 74b4aa3

Browse files
committed
initial commit
1 parent 326f234 commit 74b4aa3

File tree

6 files changed

+533
-880
lines changed

6 files changed

+533
-880
lines changed

apps/price_pusher/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"@injectivelabs/networks": "^1.14.6",
6262
"@injectivelabs/sdk-ts": "1.10.72",
6363
"@mysten/sui": "^1.3.0",
64-
"@pythnetwork/price-service-client": "workspace:*",
64+
"@pythnetwork/hermes-client": "^1.3.1",
6565
"@pythnetwork/price-service-sdk": "workspace:^",
6666
"@pythnetwork/pyth-fuel-js": "workspace:*",
6767
"@pythnetwork/pyth-sdk-solidity": "workspace:*",

apps/price_pusher/src/controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UnixTimestamp } from "@pythnetwork/price-service-client";
1+
import { UnixTimestamp } from "@pythnetwork/hermes-client";
22
import { DurationInSeconds, sleep } from "./utils";
33
import { IPriceListener, IPricePusher } from "./interface";
44
import { PriceConfig, shouldUpdate, UpdateCondition } from "./price-config";

apps/price_pusher/src/interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HexString, UnixTimestamp } from "@pythnetwork/price-service-client";
1+
import { HexString, UnixTimestamp } from "@pythnetwork/hermes-client";
22
import { DurationInSeconds } from "./utils";
33

44
export type PriceItem = {

apps/price_pusher/src/price-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { HexString } from "@pythnetwork/price-service-client";
1+
import { HexString } from "@pythnetwork/hermes-client";
22
import Joi from "joi";
33
import YAML from "yaml";
44
import fs from "fs";

apps/price_pusher/src/pyth-price-listener.ts

Lines changed: 19 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import {
22
HexString,
3-
PriceFeed,
4-
PriceServiceConnection,
5-
} from "@pythnetwork/price-service-client";
3+
HermesClient,
4+
PriceUpdate,
5+
} from "@pythnetwork/hermes-client";
66
import { PriceInfo, IPriceListener, PriceItem } from "./interface";
77
import { Logger } from "pino";
8+
import { PriceFeed } from "@pythnetwork/price-service-sdk";
89

910
type TimestampInMs = number & { readonly _: unique symbol };
1011

1112
export class PythPriceListener implements IPriceListener {
12-
private connection: PriceServiceConnection;
13+
private connection: HermesClient;
1314
private priceIds: HexString[];
1415
private priceIdToAlias: Map<HexString, string>;
1516
private latestPriceInfo: Map<HexString, PriceInfo>;
@@ -18,7 +19,7 @@ export class PythPriceListener implements IPriceListener {
1819
private healthCheckInterval?: NodeJS.Timeout;
1920

2021
constructor(
21-
connection: PriceServiceConnection,
22+
connection: HermesClient,
2223
priceItems: PriceItem[],
2324
logger: Logger
2425
) {
@@ -34,107 +35,29 @@ export class PythPriceListener implements IPriceListener {
3435
// This method should be awaited on and once it finishes it has the latest value
3536
// for the given price feeds (if they exist).
3637
async start() {
37-
// Set custom error handler for websocket errors
38-
this.connection.onWsError = (error: Error) => {
39-
if (error.message.includes("not found")) {
40-
// Extract invalid feed IDs from error message
41-
const match = error.message.match(/\[(.*?)\]/);
42-
if (match) {
43-
const invalidFeedIds = match[1].split(",").map((id) => {
44-
// Remove '0x' prefix if present to match our stored IDs
45-
return id.trim().replace(/^0x/, "");
46-
});
47-
48-
// Log invalid feeds with their aliases
49-
invalidFeedIds.forEach((id) => {
50-
this.logger.error(
51-
`Price feed ${id} (${this.priceIdToAlias.get(
52-
id
53-
)}) not found for subscribePriceFeedUpdates`
54-
);
55-
});
56-
57-
// Filter out invalid feeds and resubscribe with valid ones
58-
const validFeeds = this.priceIds.filter(
59-
(id) => !invalidFeedIds.includes(id)
60-
);
61-
62-
this.priceIds = validFeeds;
63-
64-
if (validFeeds.length > 0) {
65-
this.logger.info("Resubscribing with valid feeds only");
66-
this.connection.subscribePriceFeedUpdates(
67-
validFeeds,
68-
this.onNewPriceFeed.bind(this)
69-
);
70-
}
71-
}
72-
} else {
73-
this.logger.error("Websocket error occurred:", error);
74-
}
75-
};
76-
77-
this.connection.subscribePriceFeedUpdates(
38+
this.connection.getPriceUpdatesStream(
7839
this.priceIds,
79-
this.onNewPriceFeed.bind(this)
8040
);
8141

8242
try {
83-
const priceFeeds = await this.connection.getLatestPriceFeeds(
84-
this.priceIds
43+
const priceUpdates = await this.connection.getLatestPriceUpdates(
44+
this.priceIds,
45+
{
46+
encoding: "hex",
47+
parsed: true,
48+
ignoreInvalidPriceIds: true,
49+
}
8550
);
86-
priceFeeds?.forEach((priceFeed) => {
87-
const latestAvailablePrice = priceFeed.getPriceUnchecked();
88-
this.latestPriceInfo.set(priceFeed.id, {
89-
price: latestAvailablePrice.price,
90-
conf: latestAvailablePrice.conf,
91-
publishTime: latestAvailablePrice.publishTime,
51+
priceUpdates.parsed?.forEach((priceUpdate) => {
52+
this.latestPriceInfo.set(priceUpdate.id, {
53+
price: priceUpdate.price.price,
54+
conf: priceUpdate.price.conf,
55+
publishTime: priceUpdate.price.publish_time,
9256
});
9357
});
9458
} catch (error: any) {
9559
// Always log the HTTP error first
9660
this.logger.error("Failed to get latest price feeds:", error);
97-
98-
if (error.response.data.includes("Price ids not found:")) {
99-
// Extract invalid feed IDs from error message
100-
const invalidFeedIds = error.response.data
101-
.split("Price ids not found:")[1]
102-
.split(",")
103-
.map((id: string) => id.trim().replace(/^0x/, ""));
104-
105-
// Log invalid feeds with their aliases
106-
invalidFeedIds.forEach((id: string) => {
107-
this.logger.error(
108-
`Price feed ${id} (${this.priceIdToAlias.get(
109-
id
110-
)}) not found for getLatestPriceFeeds`
111-
);
112-
});
113-
114-
// Filter out invalid feeds and retry
115-
const validFeeds = this.priceIds.filter(
116-
(id) => !invalidFeedIds.includes(id)
117-
);
118-
119-
this.priceIds = validFeeds;
120-
121-
if (validFeeds.length > 0) {
122-
this.logger.info(
123-
"Retrying getLatestPriceFeeds with valid feeds only"
124-
);
125-
const validPriceFeeds = await this.connection.getLatestPriceFeeds(
126-
validFeeds
127-
);
128-
validPriceFeeds?.forEach((priceFeed) => {
129-
const latestAvailablePrice = priceFeed.getPriceUnchecked();
130-
this.latestPriceInfo.set(priceFeed.id, {
131-
price: latestAvailablePrice.price,
132-
conf: latestAvailablePrice.conf,
133-
publishTime: latestAvailablePrice.publishTime,
134-
});
135-
});
136-
}
137-
}
13861
}
13962

14063
// Store health check interval reference
@@ -148,29 +71,6 @@ export class PythPriceListener implements IPriceListener {
14871
}, 5000);
14972
}
15073

151-
private onNewPriceFeed(priceFeed: PriceFeed) {
152-
this.logger.debug(
153-
`Received new price feed update from Pyth price service: ${this.priceIdToAlias.get(
154-
priceFeed.id
155-
)} ${priceFeed.id}`
156-
);
157-
158-
// Consider price to be currently available if it is not older than 60s
159-
const currentPrice = priceFeed.getPriceNoOlderThan(60);
160-
if (currentPrice === undefined) {
161-
return;
162-
}
163-
164-
const priceInfo: PriceInfo = {
165-
conf: currentPrice.conf,
166-
price: currentPrice.price,
167-
publishTime: currentPrice.publishTime,
168-
};
169-
170-
this.latestPriceInfo.set(priceFeed.id, priceInfo);
171-
this.lastUpdated = Date.now() as TimestampInMs;
172-
}
173-
17474
getLatestPriceInfo(priceId: string): PriceInfo | undefined {
17575
return this.latestPriceInfo.get(priceId);
17676
}

0 commit comments

Comments
 (0)