Skip to content

Commit 40cf49e

Browse files
author
Dev Kalra
authored
[price-pusher] refactor price listener (#634)
* update pyth-evm-js dependency to pyth-common-js * refactor price listener
1 parent 20e8e15 commit 40cf49e

File tree

6 files changed

+87
-104
lines changed

6 files changed

+87
-104
lines changed

price_pusher/src/controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { UnixTimestamp } from "@pythnetwork/pyth-evm-js";
22
import { DurationInSeconds, sleep } from "./utils";
3-
import { ChainPricePusher, PriceListener } from "./interface";
3+
import { ChainPricePusher, IPriceListener } from "./interface";
44
import { PriceConfig, shouldUpdate } from "./price-config";
55

66
export class Controller {
77
private cooldownDuration: DurationInSeconds;
88
constructor(
99
private priceConfigs: PriceConfig[],
10-
private sourcePriceListener: PriceListener,
11-
private targetPriceListener: PriceListener,
10+
private sourcePriceListener: IPriceListener,
11+
private targetPriceListener: IPriceListener,
1212
private targetChainPricePusher: ChainPricePusher,
1313
config: {
1414
cooldownDuration: DurationInSeconds;

price_pusher/src/evm.ts

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
} from "@pythnetwork/pyth-evm-js";
66
import { Contract, EventData } from "web3-eth-contract";
77
import { PriceConfig } from "./price-config";
8-
import { ChainPricePusher, PriceInfo, PriceListener } from "./interface";
8+
import { ChainPricePusher, PriceInfo, ChainPriceListener } from "./interface";
99
import { TransactionReceipt } from "ethereum-protocol";
1010
import { addLeading0x, DurationInSeconds, removeLeading0x } from "./utils";
1111
import AbstractPythAbi from "@pythnetwork/pyth-sdk-solidity/abis/AbstractPyth.json";
@@ -14,30 +14,27 @@ import { Provider } from "web3/providers";
1414
import Web3 from "web3";
1515
import { isWsEndpoint } from "./utils";
1616

17-
export class EvmPriceListener implements PriceListener {
17+
export class EvmPriceListener extends ChainPriceListener {
1818
private pythContractFactory: PythContractFactory;
1919
private pythContract: Contract;
20-
private latestPriceInfo: Map<HexString, PriceInfo>;
21-
private priceIds: HexString[];
2220
private priceIdToAlias: Map<HexString, string>;
2321

24-
private pollingFrequency: DurationInSeconds;
25-
2622
constructor(
2723
pythContractFactory: PythContractFactory,
2824
priceConfigs: PriceConfig[],
2925
config: {
3026
pollingFrequency: DurationInSeconds;
3127
}
3228
) {
33-
this.latestPriceInfo = new Map();
34-
this.priceIds = priceConfigs.map((priceConfig) => priceConfig.id);
29+
super(
30+
"Evm",
31+
config.pollingFrequency,
32+
priceConfigs.map((priceConfig) => priceConfig.id)
33+
);
3534
this.priceIdToAlias = new Map(
3635
priceConfigs.map((priceConfig) => [priceConfig.id, priceConfig.alias])
3736
);
3837

39-
this.pollingFrequency = config.pollingFrequency;
40-
4138
this.pythContractFactory = pythContractFactory;
4239
this.pythContract = this.pythContractFactory.createPythContract();
4340
}
@@ -55,10 +52,8 @@ export class EvmPriceListener implements PriceListener {
5552
);
5653
}
5754

58-
console.log(`Polling the prices every ${this.pollingFrequency} seconds...`);
59-
setInterval(this.pollPrices.bind(this), this.pollingFrequency * 1000);
60-
61-
await this.pollPrices();
55+
// base class for polling
56+
await super.start();
6257
}
6358

6459
private async startSubscription() {
@@ -97,20 +92,6 @@ export class EvmPriceListener implements PriceListener {
9792
this.updateLatestPriceInfo(priceId, priceInfo);
9893
}
9994

100-
private async pollPrices() {
101-
console.log("Polling evm prices...");
102-
for (const priceId of this.priceIds) {
103-
const currentPriceInfo = await this.getOnChainPriceInfo(priceId);
104-
if (currentPriceInfo !== undefined) {
105-
this.updateLatestPriceInfo(priceId, currentPriceInfo);
106-
}
107-
}
108-
}
109-
110-
getLatestPriceInfo(priceId: string): PriceInfo | undefined {
111-
return this.latestPriceInfo.get(priceId);
112-
}
113-
11495
async getOnChainPriceInfo(
11596
priceId: HexString
11697
): Promise<PriceInfo | undefined> {
@@ -131,22 +112,6 @@ export class EvmPriceListener implements PriceListener {
131112
publishTime: Number(priceRaw.publishTime),
132113
};
133114
}
134-
135-
private updateLatestPriceInfo(priceId: HexString, observedPrice: PriceInfo) {
136-
const cachedLatestPriceInfo = this.getLatestPriceInfo(priceId);
137-
138-
// Ignore the observed price if the cache already has newer
139-
// price. This could happen because we are using polling and
140-
// subscription at the same time.
141-
if (
142-
cachedLatestPriceInfo !== undefined &&
143-
cachedLatestPriceInfo.publishTime > observedPrice.publishTime
144-
) {
145-
return;
146-
}
147-
148-
this.latestPriceInfo.set(priceId, observedPrice);
149-
}
150115
}
151116

152117
export class EvmPricePusher implements ChainPricePusher {

price_pusher/src/injective.ts

Lines changed: 7 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { HexString, PriceServiceConnection } from "@pythnetwork/pyth-common-js";
2-
import { ChainPricePusher, PriceInfo, PriceListener } from "./interface";
2+
import { ChainPricePusher, PriceInfo, ChainPriceListener } from "./interface";
33
import { DurationInSeconds } from "./utils";
44
import { PriceConfig } from "./price-config";
55
import {
@@ -32,13 +32,7 @@ type UpdateFeeResponse = {
3232
};
3333

3434
// this use price without leading 0x
35-
// FIXME: implement common methods in the parent class
36-
export class InjectivePriceListener implements PriceListener {
37-
private latestPriceInfo: Map<HexString, PriceInfo>;
38-
private priceIds: HexString[];
39-
40-
private pollingFrequency: DurationInSeconds;
41-
35+
export class InjectivePriceListener extends ChainPriceListener {
4236
constructor(
4337
private contractAddress: string,
4438
private grpcEndpoint: string,
@@ -47,27 +41,11 @@ export class InjectivePriceListener implements PriceListener {
4741
pollingFrequency: DurationInSeconds;
4842
}
4943
) {
50-
this.latestPriceInfo = new Map();
51-
this.priceIds = priceConfigs.map((priceConfig) => priceConfig.id);
52-
53-
this.pollingFrequency = config.pollingFrequency;
54-
}
55-
56-
async start() {
57-
console.log(`Polling the prices every ${this.pollingFrequency} seconds...`);
58-
setInterval(this.pollPrices.bind(this), this.pollingFrequency * 1000);
59-
60-
await this.pollPrices();
61-
}
62-
63-
private async pollPrices() {
64-
console.log("Polling injective prices...");
65-
for (const priceId of this.priceIds) {
66-
const currentPriceInfo = await this.getOnChainPriceInfo(priceId);
67-
if (currentPriceInfo !== undefined) {
68-
this.updateLatestPriceInfo(priceId, currentPriceInfo);
69-
}
70-
}
44+
super(
45+
"Injective",
46+
config.pollingFrequency,
47+
priceConfigs.map((priceConfig) => priceConfig.id)
48+
);
7149
}
7250

7351
async getOnChainPriceInfo(
@@ -95,26 +73,6 @@ export class InjectivePriceListener implements PriceListener {
9573
publishTime: priceQueryResponse.price_feed.price.publish_time,
9674
};
9775
}
98-
99-
private updateLatestPriceInfo(priceId: HexString, observedPrice: PriceInfo) {
100-
const cachedLatestPriceInfo = this.getLatestPriceInfo(priceId);
101-
102-
// Ignore the observed price if the cache already has newer
103-
// price. This could happen because we are using polling and
104-
// subscription at the same time.
105-
if (
106-
cachedLatestPriceInfo !== undefined &&
107-
cachedLatestPriceInfo.publishTime > observedPrice.publishTime
108-
) {
109-
return;
110-
}
111-
112-
this.latestPriceInfo.set(priceId, observedPrice);
113-
}
114-
115-
getLatestPriceInfo(priceId: string): PriceInfo | undefined {
116-
return this.latestPriceInfo.get(priceId);
117-
}
11876
}
11977

12078
export class InjectivePricePusher implements ChainPricePusher {

price_pusher/src/interface.ts

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,71 @@
1-
import { HexString, UnixTimestamp } from "@pythnetwork/pyth-evm-js";
1+
import { HexString, UnixTimestamp } from "@pythnetwork/pyth-common-js";
2+
import { DurationInSeconds } from "./utils";
23

34
export type PriceInfo = {
45
price: string;
56
conf: string;
67
publishTime: UnixTimestamp;
78
};
89

9-
export interface PriceListener {
10+
export interface IPriceListener {
11+
getLatestPriceInfo(priceId: string): PriceInfo | undefined;
12+
}
13+
14+
export abstract class ChainPriceListener implements IPriceListener {
15+
private latestPriceInfo: Map<HexString, PriceInfo>;
16+
17+
constructor(
18+
private chain: string,
19+
private pollingFrequency: DurationInSeconds,
20+
protected priceIds: HexString[]
21+
) {
22+
this.latestPriceInfo = new Map();
23+
}
24+
25+
async start() {
26+
console.log(`Polling the prices every ${this.pollingFrequency} seconds...`);
27+
setInterval(this.pollPrices.bind(this), this.pollingFrequency * 1000);
28+
29+
await this.pollPrices();
30+
}
31+
32+
private async pollPrices() {
33+
console.log(`Polling ${this.chain} prices...`);
34+
for (const priceId of this.priceIds) {
35+
const currentPriceInfo = await this.getOnChainPriceInfo(priceId);
36+
if (currentPriceInfo !== undefined) {
37+
this.updateLatestPriceInfo(priceId, currentPriceInfo);
38+
}
39+
}
40+
}
41+
42+
protected updateLatestPriceInfo(
43+
priceId: HexString,
44+
observedPrice: PriceInfo
45+
) {
46+
const cachedLatestPriceInfo = this.getLatestPriceInfo(priceId);
47+
48+
// Ignore the observed price if the cache already has newer
49+
// price. This could happen because we are using polling and
50+
// subscription at the same time.
51+
if (
52+
cachedLatestPriceInfo !== undefined &&
53+
cachedLatestPriceInfo.publishTime > observedPrice.publishTime
54+
) {
55+
return;
56+
}
57+
58+
this.latestPriceInfo.set(priceId, observedPrice);
59+
}
60+
1061
// Should return undefined only when the price does not exist.
11-
getLatestPriceInfo(priceId: HexString): undefined | PriceInfo;
62+
getLatestPriceInfo(priceId: string): PriceInfo | undefined {
63+
return this.latestPriceInfo.get(priceId);
64+
}
65+
66+
abstract getOnChainPriceInfo(
67+
priceId: HexString
68+
): Promise<PriceInfo | undefined>;
1269
}
1370

1471
export interface ChainPricePusher {

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/pyth-evm-js";
1+
import { HexString } from "@pythnetwork/pyth-common-js";
22
import Joi from "joi";
33
import YAML from "yaml";
44
import fs from "fs";

price_pusher/src/pyth-price-listener.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import { HexString, PriceFeed } from "@pythnetwork/pyth-evm-js";
2-
import { PriceServiceConnection } from "@pythnetwork/pyth-common-js";
1+
import {
2+
PriceServiceConnection,
3+
HexString,
4+
PriceFeed,
5+
} from "@pythnetwork/pyth-common-js";
36
import { PriceConfig } from "./price-config";
4-
import { PriceInfo, PriceListener } from "./interface";
7+
import { PriceInfo, IPriceListener } from "./interface";
58

6-
export class PythPriceListener implements PriceListener {
9+
export class PythPriceListener implements IPriceListener {
710
private connection: PriceServiceConnection;
811
private priceIds: HexString[];
912
private priceIdToAlias: Map<HexString, string>;

0 commit comments

Comments
 (0)