Skip to content
This repository was archived by the owner on Apr 3, 2023. It is now read-only.

Commit 4a86c5a

Browse files
authored
Update code structure (#29)
* Update code structure It makes the logic simpler and easier to follow, based on Jayant suggestions. Web3 is created in multiple places because HDWalletProvider (even loaded with Websocket) does not support subscriptions. * Address review comments
1 parent 7cc874c commit 4a86c5a

File tree

8 files changed

+427
-278
lines changed

8 files changed

+427
-278
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { HexString, Price, PriceFeed } from "@pythnetwork/pyth-evm-js";
2+
3+
import AbstractPythAbi from "@pythnetwork/pyth-sdk-solidity/abis/AbstractPyth.json";
4+
import Web3 from "web3";
5+
import { Contract, EventData } from "web3-eth-contract";
6+
import { PriceInfo, PriceListener } from "./price-listener";
7+
import {
8+
addLeading0x,
9+
DurationInSeconds,
10+
isWsEndpoint,
11+
removeLeading0x,
12+
sleep,
13+
statusNumberToEnum,
14+
} from "./utils";
15+
16+
export class EvmPriceListener implements PriceListener {
17+
private pythContract: Contract;
18+
private latestPriceInfo: Map<HexString, PriceInfo>;
19+
private priceIds: HexString[];
20+
21+
private isWs: boolean;
22+
private pollingFrequency: DurationInSeconds;
23+
24+
constructor(
25+
endpoint: string,
26+
pythContractAddr: string,
27+
priceIds: HexString[],
28+
config: {
29+
pollingFrequency: DurationInSeconds;
30+
}
31+
) {
32+
this.latestPriceInfo = new Map();
33+
this.priceIds = priceIds;
34+
35+
this.pollingFrequency = config.pollingFrequency;
36+
37+
const web3 = new Web3(endpoint);
38+
this.isWs = isWsEndpoint(endpoint);
39+
40+
this.pythContract = new web3.eth.Contract(
41+
AbstractPythAbi as any,
42+
pythContractAddr
43+
);
44+
}
45+
46+
// This method should be awaited on and once it finishes it has the latest value
47+
// for the given price feeds (if they exist).
48+
async start() {
49+
if (this.isWs) {
50+
console.log("Subscribing to the target network pyth contract events...");
51+
this.startSubscription();
52+
} else {
53+
console.log(
54+
"The target network RPC endpoint is not Websocket. Using polling instead..."
55+
);
56+
setInterval(this.pollPrices.bind(this), this.pollingFrequency * 1000);
57+
}
58+
59+
// Poll the prices to have values in the beginning until updates arrive.
60+
console.log(
61+
"Polling the prices in the beginning in order to set the initial values."
62+
);
63+
await this.pollPrices();
64+
}
65+
66+
private async startSubscription() {
67+
for (let priceId of this.priceIds) {
68+
this.pythContract.events.PriceFeedUpdate(
69+
{
70+
filter: {
71+
id: addLeading0x(priceId),
72+
fresh: true,
73+
},
74+
},
75+
this.onPriceFeedUpdate.bind(this)
76+
);
77+
}
78+
}
79+
80+
private onPriceFeedUpdate(err: Error | null, event: EventData) {
81+
if (err !== null) {
82+
console.error("PriceFeedUpdate EventEmitter received an error.");
83+
console.error(err);
84+
return;
85+
}
86+
87+
const priceId = removeLeading0x(event.returnValues.id);
88+
console.log(
89+
`Received a new Evm PriceFeedUpdate event for price feed with id ${priceId}`
90+
);
91+
92+
const priceInfo: PriceInfo = {
93+
conf: event.returnValues.conf,
94+
price: event.returnValues.price,
95+
publishTime: Number(event.returnValues.publishTime),
96+
};
97+
98+
this.latestPriceInfo.set(priceId, priceInfo);
99+
}
100+
101+
private async pollPrices() {
102+
console.log("Polling evm prices...");
103+
for (let priceId of this.priceIds) {
104+
const currentPriceInfo = await this.getOnChainPriceInfo(priceId);
105+
if (currentPriceInfo !== undefined) {
106+
this.latestPriceInfo.set(priceId, currentPriceInfo);
107+
}
108+
}
109+
}
110+
111+
getLatestPriceInfo(priceId: string): PriceInfo | undefined {
112+
return this.latestPriceInfo.get(priceId);
113+
}
114+
115+
async getOnChainPriceInfo(
116+
priceId: HexString
117+
): Promise<PriceInfo | undefined> {
118+
let priceFeedRaw;
119+
try {
120+
priceFeedRaw = await this.pythContract.methods
121+
.queryPriceFeed(addLeading0x(priceId))
122+
.call();
123+
} catch (e) {
124+
console.error(`Getting on-chain price for ${priceId} failed. Error:`);
125+
console.error(e);
126+
return undefined;
127+
}
128+
129+
const priceFeed = new PriceFeed({
130+
id: removeLeading0x(priceFeedRaw.id),
131+
productId: removeLeading0x(priceFeedRaw.productId),
132+
price: priceFeedRaw.price,
133+
conf: priceFeedRaw.conf,
134+
expo: Number(priceFeedRaw.expo),
135+
status: statusNumberToEnum(Number(priceFeedRaw.status)),
136+
maxNumPublishers: Number(priceFeedRaw.maxNumPublishers),
137+
numPublishers: Number(priceFeedRaw.numPublishers),
138+
emaPrice: priceFeedRaw.emaPrice,
139+
emaConf: priceFeedRaw.emaConf,
140+
publishTime: Number(priceFeedRaw.publishTime),
141+
prevPrice: priceFeedRaw.prevPrice,
142+
prevConf: priceFeedRaw.prevConf,
143+
prevPublishTime: Number(priceFeedRaw.prevPublishTime),
144+
});
145+
146+
const prevPrice = priceFeed.getPrevPriceUnchecked();
147+
148+
return {
149+
conf: prevPrice[0].conf,
150+
price: prevPrice[0].price,
151+
publishTime: prevPrice[1],
152+
};
153+
}
154+
}

pyth-evm-price-pusher/src/handler.ts

Lines changed: 0 additions & 116 deletions
This file was deleted.

0 commit comments

Comments
 (0)