[price-pusher] refactor index page (#636)

* refactor index page

* remove pythcontractaddr

* address comments

* bug fix
This commit is contained in:
Dev Kalra
2023-02-28 15:07:38 +05:30
committed by GitHub
parent c0b801ef15
commit 6bd4e2d3b8
10 changed files with 177 additions and 107 deletions

78
package-lock.json generated
View File

@@ -11095,6 +11095,36 @@
"resolved": "target_chains/aptos/sdk/js",
"link": true
},
"node_modules/@pythnetwork/pyth-common-js": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-common-js/-/pyth-common-js-1.4.0.tgz",
"integrity": "sha512-ilK+0/+tivMVPMIFmup+UfUHklhsS2fqofZxS2+XCn4WBJfI0lIKtiAaBVjV7WmzmC2mDjujcTCDn4RbqpLVqg==",
"dependencies": {
"@pythnetwork/pyth-sdk-js": "^1.2.0",
"@types/ws": "^8.5.3",
"axios": "^0.26.1",
"axios-retry": "^3.2.4",
"isomorphic-ws": "^4.0.1",
"ts-log": "^2.2.4",
"ws": "^8.6.0"
}
},
"node_modules/@pythnetwork/pyth-common-js/node_modules/@types/ws": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
"integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@pythnetwork/pyth-common-js/node_modules/axios": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"dependencies": {
"follow-redirects": "^1.14.8"
}
},
"node_modules/@pythnetwork/pyth-evm-contract": {
"resolved": "target_chains/ethereum/contracts",
"link": true
@@ -11111,6 +11141,11 @@
"resolved": "governance/multisig_wh_message_builder",
"link": true
},
"node_modules/@pythnetwork/pyth-sdk-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-js/-/pyth-sdk-js-1.2.0.tgz",
"integrity": "sha512-grh6YCkp/nH73ACNu+Mew64lLVgz6egVBJm8JvdNkRggWkUn1PE4ZvV/6ceTIui5OhI369qzMCkldiAlIMBjnQ=="
},
"node_modules/@pythnetwork/pyth-sdk-solidity": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-2.2.0.tgz",
@@ -47823,8 +47858,7 @@
"license": "Apache-2.0",
"dependencies": {
"@injectivelabs/sdk-ts": "^1.0.457",
"@pythnetwork/price-service-client": "*",
"@pythnetwork/pyth-evm-js": "^1.1.0",
"@pythnetwork/pyth-common-js": "^1.4.0",
"@pythnetwork/pyth-sdk-solidity": "^2.2.0",
"@truffle/hdwallet-provider": "^2.1.3",
"joi": "^17.6.0",
@@ -57546,6 +57580,38 @@
}
}
},
"@pythnetwork/pyth-common-js": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-common-js/-/pyth-common-js-1.4.0.tgz",
"integrity": "sha512-ilK+0/+tivMVPMIFmup+UfUHklhsS2fqofZxS2+XCn4WBJfI0lIKtiAaBVjV7WmzmC2mDjujcTCDn4RbqpLVqg==",
"requires": {
"@pythnetwork/pyth-sdk-js": "^1.2.0",
"@types/ws": "^8.5.3",
"axios": "^0.26.1",
"axios-retry": "^3.2.4",
"isomorphic-ws": "^4.0.1",
"ts-log": "^2.2.4",
"ws": "^8.6.0"
},
"dependencies": {
"@types/ws": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
"integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
"requires": {
"@types/node": "*"
}
},
"axios": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"requires": {
"follow-redirects": "^1.14.8"
}
}
}
},
"@pythnetwork/pyth-evm-contract": {
"version": "file:target_chains/ethereum/contracts",
"requires": {
@@ -57609,8 +57675,7 @@
"version": "file:price_pusher",
"requires": {
"@injectivelabs/sdk-ts": "^1.0.457",
"@pythnetwork/price-service-client": "*",
"@pythnetwork/pyth-evm-js": "^1.1.0",
"@pythnetwork/pyth-common-js": "^1.4.0",
"@pythnetwork/pyth-sdk-solidity": "^2.2.0",
"@truffle/hdwallet-provider": "^2.1.3",
"@types/ethereum-protocol": "^1.0.2",
@@ -59529,6 +59594,11 @@
}
}
},
"@pythnetwork/pyth-sdk-js": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-js/-/pyth-sdk-js-1.2.0.tgz",
"integrity": "sha512-grh6YCkp/nH73ACNu+Mew64lLVgz6egVBJm8JvdNkRggWkUn1PE4ZvV/6ceTIui5OhI369qzMCkldiAlIMBjnQ=="
},
"@pythnetwork/pyth-sdk-solidity": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-2.2.0.tgz",

View File

@@ -47,8 +47,7 @@
},
"dependencies": {
"@injectivelabs/sdk-ts": "^1.0.457",
"@pythnetwork/price-service-client": "*",
"@pythnetwork/pyth-evm-js": "^1.1.0",
"@pythnetwork/pyth-common-js": "^1.4.0",
"@pythnetwork/pyth-sdk-solidity": "^2.2.0",
"@truffle/hdwallet-provider": "^2.1.3",
"joi": "^17.6.0",

View File

@@ -1,4 +1,4 @@
import { UnixTimestamp } from "@pythnetwork/pyth-evm-js";
import { UnixTimestamp } from "@pythnetwork/pyth-common-js";
import { DurationInSeconds, sleep } from "./utils";
import { ChainPricePusher, IPriceListener } from "./interface";
import { PriceConfig, shouldUpdate } from "./price-config";
@@ -18,6 +18,10 @@ export class Controller {
}
async start() {
// start the listeners
await this.sourcePriceListener.start();
await this.targetPriceListener.start();
for (;;) {
const pricesToPush: PriceConfig[] = [];
const pubTimesToPush: UnixTimestamp[] = [];

View File

@@ -1,8 +1,3 @@
import {
EvmPriceServiceConnection,
HexString,
UnixTimestamp,
} from "@pythnetwork/pyth-evm-js";
import { Contract, EventData } from "web3-eth-contract";
import { PriceConfig } from "./price-config";
import { ChainPricePusher, PriceInfo, ChainPriceListener } from "./interface";
@@ -13,6 +8,11 @@ import HDWalletProvider from "@truffle/hdwallet-provider";
import { Provider } from "web3/providers";
import Web3 from "web3";
import { isWsEndpoint } from "./utils";
import {
PriceServiceConnection,
HexString,
UnixTimestamp,
} from "@pythnetwork/pyth-common-js";
export class EvmPriceListener extends ChainPriceListener {
private pythContractFactory: PythContractFactory;
@@ -116,7 +116,7 @@ export class EvmPriceListener extends ChainPriceListener {
export class EvmPricePusher implements ChainPricePusher {
constructor(
private connection: EvmPriceServiceConnection,
private connection: PriceServiceConnection,
private pythContract: Contract
) {}
// The pubTimes are passed here to use the values that triggered the push.
@@ -135,7 +135,7 @@ export class EvmPricePusher implements ChainPricePusher {
const priceIdsWith0x = priceIds.map((priceId) => addLeading0x(priceId));
const priceFeedUpdateData = await this.connection.getPriceFeedsUpdateData(
const priceFeedUpdateData = await this.getPriceFeedsUpdateData(
priceIdsWith0x
);
@@ -198,6 +198,15 @@ export class EvmPricePusher implements ChainPricePusher {
throw err;
});
}
private async getPriceFeedsUpdateData(
priceIds: HexString[]
): Promise<string[]> {
const latestVaas = await this.connection.getLatestVaas(priceIds);
return latestVaas.map(
(vaa) => "0x" + Buffer.from(vaa, "base64").toString("hex")
);
}
}
export class PythContractFactory {

View File

@@ -4,17 +4,14 @@
// FIXME: release a new version
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import {
EvmPriceServiceConnection,
CONTRACT_ADDR,
} from "@pythnetwork/pyth-evm-js";
import { Controller } from "./controller";
import { EvmPriceListener, EvmPricePusher, PythContractFactory } from "./evm";
import { PythPriceListener } from "./pyth-price-listener";
import fs from "fs";
import { readPriceConfigFile } from "./price-config";
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
import { PriceServiceConnection } from "@pythnetwork/pyth-common-js";
import { InjectivePriceListener, InjectivePricePusher } from "./injective";
import { ChainPricePusher, IPriceListener } from "./interface";
const argv = yargs(hideBin(process.argv))
.option("network", {
@@ -27,7 +24,7 @@ const argv = yargs(hideBin(process.argv))
description:
"RPC endpoint URL for the network. If you provide a normal HTTP endpoint, the pusher " +
"will periodically poll for updates. The polling interval is configurable via the " +
"`polling-frequency` command-line argument. for the evm chains, if you provide a websocket RPC " +
"`polling-frequency` command-line argument. For the evm chains, if you provide a websocket RPC " +
"endpoint (`ws[s]://...`), the price pusher will use event subscriptions to read " +
"the current EVM price in addition to polling. ",
type: "string",
@@ -79,102 +76,94 @@ const argv = yargs(hideBin(process.argv))
})
.parseSync();
let pythContractAddr: string;
if (CONTRACT_ADDR[argv.pythContract] !== undefined) {
pythContractAddr = CONTRACT_ADDR[argv.pythContract];
} else {
pythContractAddr = argv.pythContract;
}
const priceConfigs = readPriceConfigFile(argv.priceConfigFile);
async function injectiveRun() {
const connection = new PriceServiceConnection(argv.priceEndpoint, {
logger: console,
});
const pythPriceListener = new PythPriceListener(connection, priceConfigs);
const injectivePriceListener = new InjectivePriceListener(
pythContractAddr,
argv.endpoint,
priceConfigs,
{ pollingFrequency: argv.pollingFrequency }
);
const injectivePricePusher = new InjectivePricePusher(
connection,
argv.pythContract,
argv.endpoint,
fs.readFileSync(argv.mnemonicFile, "utf-8").trim()
);
// TODO: name ChainPricePusher -> IPricePusher in a clean up PR
// TODO: update listeners to not depend on the whole priceConfig
async function start({
sourcePriceListener,
targetPriceListener,
targetPricePusher,
}: {
sourcePriceListener: IPriceListener;
targetPriceListener: IPriceListener;
targetPricePusher: ChainPricePusher;
}) {
const handler = new Controller(
priceConfigs,
pythPriceListener,
injectivePriceListener,
injectivePricePusher,
sourcePriceListener,
targetPriceListener,
targetPricePusher,
{
cooldownDuration: argv.cooldownDuration,
}
);
await injectivePriceListener.start();
await pythPriceListener.start();
// Handler starts after the above listeners are started
// which means that they have fetched their initial price information.
await handler.start();
}
async function evmRun() {
const connection = new EvmPriceServiceConnection(argv.priceEndpoint, {
logger: console,
});
const priceServiceConnection = new PriceServiceConnection(argv.priceEndpoint, {
logger: console,
});
const pythContractFactory = new PythContractFactory(
argv.endpoint,
fs.readFileSync(argv.mnemonicFile, "utf-8").trim(),
pythContractAddr
);
const pythPriceListener = new PythPriceListener(
priceServiceConnection,
priceConfigs
);
const evmPriceListener = new EvmPriceListener(
pythContractFactory,
priceConfigs,
{
pollingFrequency: argv.pollingFrequency,
function getNetworkPriceListener(network: string): IPriceListener {
switch (network) {
case "evm": {
const pythContractFactory = new PythContractFactory(
argv.endpoint,
fs.readFileSync(argv.mnemonicFile, "utf-8").trim(),
argv.pythContract
);
return new EvmPriceListener(pythContractFactory, priceConfigs, {
pollingFrequency: argv.pollingFrequency,
});
}
);
const pythPriceListener = new PythPriceListener(connection, priceConfigs);
const evmPricePusher = new EvmPricePusher(
connection,
pythContractFactory.createPythContractWithPayer()
);
const handler = new Controller(
priceConfigs,
pythPriceListener,
evmPriceListener,
evmPricePusher,
{
cooldownDuration: argv.cooldownDuration,
}
);
await evmPriceListener.start();
await pythPriceListener.start();
// Handler starts after the above listeners are started
// which means that they have fetched their initial price information.
await handler.start();
case "injective":
return new InjectivePriceListener(
argv.pythContract,
argv.endpoint,
priceConfigs,
{ pollingFrequency: argv.pollingFrequency }
);
default:
throw new Error("invalid network");
}
}
function run() {
if (argv.network === "injective") injectiveRun();
else if (argv.network === "evm") evmRun();
function getNetworkPricePusher(network: string): ChainPricePusher {
switch (network) {
case "evm": {
const pythContractFactory = new PythContractFactory(
argv.endpoint,
fs.readFileSync(argv.mnemonicFile, "utf-8").trim(),
argv.pythContract
);
return new EvmPricePusher(
priceServiceConnection,
pythContractFactory.createPythContractWithPayer()
);
}
case "injective":
return new InjectivePricePusher(
priceServiceConnection,
argv.pythContract,
argv.endpoint,
fs.readFileSync(argv.mnemonicFile, "utf-8").trim()
);
default:
throw new Error("invalid network");
}
}
run();
start({
sourcePriceListener: pythPriceListener,
targetPriceListener: getNetworkPriceListener(argv.network),
targetPricePusher: getNetworkPricePusher(argv.network),
});

View File

@@ -1,7 +1,4 @@
import {
HexString,
PriceServiceConnection,
} from "@pythnetwork/price-service-client";
import { HexString, PriceServiceConnection } from "@pythnetwork/pyth-common-js";
import { ChainPricePusher, PriceInfo, ChainPriceListener } from "./interface";
import { DurationInSeconds } from "./utils";
import { PriceConfig } from "./price-config";

View File

@@ -1,4 +1,4 @@
import { HexString, UnixTimestamp } from "@pythnetwork/price-service-client";
import { HexString, UnixTimestamp } from "@pythnetwork/pyth-common-js";
import { DurationInSeconds } from "./utils";
export type PriceInfo = {
@@ -8,6 +8,8 @@ export type PriceInfo = {
};
export interface IPriceListener {
// start fetches the latest price initially and then keep updating it
start(): Promise<void>;
getLatestPriceInfo(priceId: string): PriceInfo | undefined;
}

View File

@@ -1,4 +1,4 @@
import { HexString } from "@pythnetwork/price-service-client";
import { HexString } from "@pythnetwork/pyth-common-js";
import Joi from "joi";
import YAML from "yaml";
import fs from "fs";

View File

@@ -2,7 +2,7 @@ import {
HexString,
PriceFeed,
PriceServiceConnection,
} from "@pythnetwork/price-service-client";
} from "@pythnetwork/pyth-common-js";
import { PriceConfig } from "./price-config";
import { PriceInfo, IPriceListener } from "./interface";

View File

@@ -1,4 +1,4 @@
import { HexString } from "@pythnetwork/pyth-evm-js";
import { HexString } from "@pythnetwork/pyth-common-js";
export type PctNumber = number;
export type DurationInSeconds = number;