[price-pusher] refactor (#648)
* add a dev command for ease * implement a new command structure * remove comments * move files * add config * update readme * testnet compose files update * update mainnet compose file
This commit is contained in:
parent
deb804617b
commit
f5620ecbd2
|
@ -48,11 +48,22 @@ To run the price pusher, please run the following commands, replacing the comman
|
|||
```sh
|
||||
npm install # Only run it the first time
|
||||
|
||||
npm run start -- --evm-endpoint wss://example-rpc.com --mnemonic-file "path/to/mnemonic.txt" \
|
||||
--pyth-contract example_network --price-endpoint https://example-pyth-price.com \
|
||||
--price-config-file "path/to/price-config-file.yaml" \
|
||||
# For EVM
|
||||
npm run start -- evm --endpoint wss://example-rpc.com \
|
||||
--pyth-contract-address 0xff1a0f4744e8582DF...... \
|
||||
--price-service-endpoint https://example-pyth-price.com \
|
||||
--price-config-file "path/to/price-config-file.yaml.testnet.sample.yaml" \
|
||||
--mnemonic-file "path/to/mnemonic.txt" \
|
||||
[--cooldown-duration 10] \
|
||||
[--evm-polling-frequency 5]
|
||||
[--polling-frequency 5]
|
||||
|
||||
# For Injective
|
||||
npm run start -- injective --grpc-endpoint https://grpc-endpoint.com \
|
||||
--pyth-contract-address inj1z60tg0... --price-service-endpoint "https://example-pyth-price.com" \
|
||||
--price-config-file "path/to/price-config-file.yaml.testnet.sample.yaml" \
|
||||
--mnemonic-file "path/to/mnemonic.txt" \
|
||||
[--cooldown-duration 10] \
|
||||
[--polling-frequency 5]
|
||||
|
||||
# Or, run the price pusher docker image instead of building from the source
|
||||
docker run public.ecr.aws/pyth-network/xc-evm-price-pusher:v<version> -- <above-arguments>
|
||||
|
@ -60,59 +71,50 @@ docker run public.ecr.aws/pyth-network/xc-evm-price-pusher:v<version> -- <above-
|
|||
|
||||
### Command Line Arguments
|
||||
|
||||
The program accepts the following command line arguments:
|
||||
To know more about the arguments the price-pusher accepts. You can run:
|
||||
|
||||
- `evm-endpoint`: RPC endpoint URL for the EVM network. If you provide a normal HTTP endpoint,
|
||||
the pusher will periodically poll for updates. The polling interval is configurable via the
|
||||
`evm-polling-frequency` command-line argument (described below). 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.
|
||||
- `mnemonic-file`: Path to payer mnemonic (private key) file.
|
||||
- `pyth-contract`: The Pyth contract address. Provide the network name on which Pyth is deployed
|
||||
or the Pyth contract address if you use a local network.
|
||||
You can find the networks on which pyth is live and their corresponding names
|
||||
[here](../pyth-evm-js/src/index.ts#L13). An example is `bnb_testnet`.
|
||||
- `price-endpoint`: Endpoint URL for the price service. You can use
|
||||
`https://xc-testnet.pyth.network` for testnet and
|
||||
`https://xc-mainnet.pyth.network` for mainnet. It is recommended
|
||||
to run a standalone price service for more resiliency.
|
||||
- `price-config-file`: Path to price configuration YAML file.
|
||||
- `cooldown-duration` (Optional): The amount of time (in seconds) to wait between pushing
|
||||
price updates. It should be greater than the block time of the network, so this
|
||||
program confirms the price is updated and does not push it twice. Default: 10 seconds.
|
||||
- `evm-polling-frequency` (Optional): The frequency to poll price info data from the EVM network
|
||||
if the RPC is not a Websocket. It has no effect if the RPC is a Websocket.
|
||||
Default: 5 seconds.
|
||||
```sh
|
||||
npm run start -- --help
|
||||
|
||||
# for specific network run
|
||||
npm run start -- {network} --help
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
For example, to push `BTC/USD` and `BNB/USD` prices on BNB testnet, run the following command:
|
||||
|
||||
```sh
|
||||
npm run start -- --evm-endpoint "https://data-seed-prebsc-1-s1.binance.org:8545" --mnemonic-file "path/to/mnemonic.txt" \
|
||||
--pyth-contract bnb_testnet --price-endpoint https://xc-testnet.pyth.network \
|
||||
--price-config-file "price-config.testnet.sample.yaml"
|
||||
npm run dev -- evm --endpoint https://endpoints.omniatech.io/v1/fantom/testnet/public \
|
||||
--pyth-contract-address 0xd7308b14BF4008e7C7196eC35610B1427C5702EA --price-service-endpoint https://xc-testnet.pyth.network \
|
||||
--mnemonic-file "./mnemonic" --price-config-file "./price-config.testnet.sample.yaml"
|
||||
```
|
||||
|
||||
[`price-config.testnet.sample.yaml`](./price-config.testnet.sample.yaml) contains configuration for `BTC/USD`
|
||||
and `BNB/USD` price feeds on Pyth testnet. [`price-config.mainnet.sample.yaml`](./price-config.mainnet.sample.yaml)
|
||||
contains the same configuration for `BTC/USD` and `BNB/USD` on Pyth mainnet.
|
||||
|
||||
You can also provide a config file instead of providing command line options, run the following command:
|
||||
|
||||
```sh
|
||||
npm run start -- injective --config "./config.injective.sample.json"
|
||||
```
|
||||
|
||||
[`config.injective.sample.json`](./config.injective.sample.json) contains configuration to publish on Injective testnet.
|
||||
|
||||
## Running using a standalone price service (via docker-compose)
|
||||
|
||||
EVM price pusher communicates with [Pyth price service][] to get the most recent price updates. Pyth price service listens to the
|
||||
Price pusher communicates with [Pyth price service][] to get the most recent price updates. Pyth price service listens to the
|
||||
Wormhole network to get latest price updates, and serves REST and websocket APIs for consumers to fetch the updates.
|
||||
Pyth hosts public endpoints for the price service; however, it is recommended to run it standalone to achieve more resiliency and
|
||||
scalability.
|
||||
|
||||
This directory contains sample docker compose files ([testnet](./docker-compose.testnet.sample.yaml),
|
||||
[mainnet](./docker-compose.mainnet.sample.yaml)) an EVM price pusher and its dependencies, including a
|
||||
[mainnet](./docker-compose.mainnet.sample.yaml)) a price pusher and its dependencies, including a
|
||||
price service and a Wormhole spy. A price service depends on a Wormhole spy. A spy listens to the Wormhole
|
||||
network and reports all Pyth-related Wormhole messages to the price service.
|
||||
|
||||
To run the services via docker-compose, please modify the your target network (testnet, mainnet) sample docker-compose file to adjust
|
||||
the path to your mnemonic file, the path to your price configuration file, the EVM endpoint, and the Pyth contract address
|
||||
as necessary.
|
||||
To run the services via docker-compose, please modify the your target network (testnet, mainnet) sample docker-compose file to adjust the configurations.
|
||||
|
||||
Then, start the docker-compose like this:
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"grpc-endpoint": "https://k8s.testnet.chain.grpc-web.injective.network",
|
||||
"pyth-contract-address": "inj1z60tg0tekdzcasenhuuwq3htjcd5slmgf7gpez",
|
||||
"price-service-endpoint": "https://xc-testnet.pyth.network",
|
||||
"mnemonic-file": "./mnemonic",
|
||||
"price-config-file": "./price-config.testnet.sample.yaml"
|
||||
}
|
|
@ -58,16 +58,14 @@ services:
|
|||
restart: always
|
||||
command:
|
||||
- "--"
|
||||
- "--evm-endpoint"
|
||||
- "--endpoint"
|
||||
# Replace this with RPC endpoint URL for the EVM network.
|
||||
- "https://bsc-dataseed2.binance.org"
|
||||
- "--mnemonic-file"
|
||||
- "/mnemonic"
|
||||
- "--pyth-contract"
|
||||
# Replace this with the Pyth contract address. Provide the network name on which Pyth is deployed
|
||||
# or the Pyth contract address if you use a local network
|
||||
- "bnb"
|
||||
- "--price-endpoint"
|
||||
- "--pyth-contract-address"
|
||||
- "0xd7308b14BF4008e7C7196eC35610B1427C5702EA"
|
||||
- "--price-service-endpoint"
|
||||
- "http://price-service:4200"
|
||||
- "--price-config-file"
|
||||
- "/price_config"
|
||||
|
|
|
@ -58,27 +58,16 @@ services:
|
|||
restart: always
|
||||
command:
|
||||
- "--"
|
||||
- "--evm-endpoint"
|
||||
# Replace this with RPC endpoint URL for the EVM network.
|
||||
- "https://data-seed-prebsc-1-s1.binance.org:8545"
|
||||
- "--mnemonic-file"
|
||||
- "/mnemonic"
|
||||
- "--pyth-contract"
|
||||
# Replace this with the Pyth contract address. Provide the network name on which Pyth is deployed
|
||||
# or the Pyth contract address if you use a local network
|
||||
- "bnb_testnet"
|
||||
- "--price-endpoint"
|
||||
- "http://price-service:4200"
|
||||
- "--price-config-file"
|
||||
- "/price_config"
|
||||
- "injective"
|
||||
# you can choose to provide all the options here or a path to the config file
|
||||
# we are providing a path to the config file
|
||||
- "--config"
|
||||
- "/command_config"
|
||||
configs:
|
||||
- mnemonic
|
||||
- price_config
|
||||
- command_config
|
||||
depends_on:
|
||||
price-service:
|
||||
condition: service_healthy
|
||||
configs:
|
||||
mnemonic:
|
||||
file: ./path/to/mnemonic.txt # Replace this with the path to the mnemonic file
|
||||
price_config:
|
||||
file: ./price-config.testnet.sample.yaml # Replace this with the path to the price configuration file
|
||||
command_config:
|
||||
file: ./config.injective.sample.json # Replace this with the path to the configuration file
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"format": "prettier --write \"src/**/*.ts\"",
|
||||
"lint": "eslint src/",
|
||||
"start": "node lib/index.js",
|
||||
"dev": "ts-node src/index.ts",
|
||||
"prepublishOnly": "npm run build && npm test && npm run lint",
|
||||
"preversion": "npm run lint",
|
||||
"version": "npm run format && git add -A src"
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
import { PriceServiceConnection } from "@pythnetwork/pyth-common-js";
|
||||
import * as options from "../options";
|
||||
import { readPriceConfigFile } from "../price-config";
|
||||
import fs from "fs";
|
||||
import { PythPriceListener } from "../pyth-price-listener";
|
||||
import { Controller } from "../controller";
|
||||
import { Options } from "yargs";
|
||||
import { EvmPriceListener, EvmPricePusher, PythContractFactory } from "./evm";
|
||||
import { getCustomGasStation } from "./custom-gas-station";
|
||||
|
||||
export default {
|
||||
command: "evm",
|
||||
describe: "run price pusher for evm",
|
||||
builder: {
|
||||
endpoint: {
|
||||
description:
|
||||
"RPC endpoint URL for evm 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. 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",
|
||||
required: true,
|
||||
} as Options,
|
||||
"custom-gas-station": {
|
||||
description:
|
||||
"If using a custom gas station, chainId of custom gas station to use",
|
||||
type: "number",
|
||||
required: false,
|
||||
} as Options,
|
||||
"tx-speed": {
|
||||
description:
|
||||
"txSpeed for custom gas station. choose between 'slow'|'standard'|'fast'",
|
||||
choices: ["slow", "standard", "fast"],
|
||||
required: false,
|
||||
} as Options,
|
||||
...options.priceConfigFile,
|
||||
...options.priceServiceEndpoint,
|
||||
...options.mnemonicFile,
|
||||
...options.pythContractAddress,
|
||||
...options.pollingFrequency,
|
||||
...options.cooldownDuration,
|
||||
},
|
||||
handler: function (argv: any) {
|
||||
// FIXME: type checks for this
|
||||
const {
|
||||
endpoint,
|
||||
priceConfigFile,
|
||||
priceServiceEndpoint,
|
||||
mnemonicFile,
|
||||
pythContractAddress,
|
||||
cooldownDuration,
|
||||
pollingFrequency,
|
||||
customGasStation,
|
||||
txSpeed,
|
||||
} = argv;
|
||||
|
||||
const priceConfigs = readPriceConfigFile(priceConfigFile);
|
||||
const priceServiceConnection = new PriceServiceConnection(
|
||||
priceServiceEndpoint,
|
||||
{
|
||||
logger: console,
|
||||
}
|
||||
);
|
||||
const mnemonic = fs.readFileSync(mnemonicFile, "utf-8").trim();
|
||||
|
||||
const priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
|
||||
|
||||
const pythListener = new PythPriceListener(
|
||||
priceServiceConnection,
|
||||
priceConfigs
|
||||
);
|
||||
|
||||
const pythContractFactory = new PythContractFactory(
|
||||
endpoint,
|
||||
mnemonic,
|
||||
pythContractAddress
|
||||
);
|
||||
|
||||
const evmListener = new EvmPriceListener(pythContractFactory, priceItems, {
|
||||
pollingFrequency,
|
||||
});
|
||||
|
||||
const gasStation = getCustomGasStation(customGasStation, txSpeed);
|
||||
const evmPusher = new EvmPricePusher(
|
||||
priceServiceConnection,
|
||||
pythContractFactory.createPythContractWithPayer(),
|
||||
gasStation
|
||||
);
|
||||
|
||||
const controller = new Controller(
|
||||
priceConfigs,
|
||||
pythListener,
|
||||
evmListener,
|
||||
evmPusher,
|
||||
{ cooldownDuration }
|
||||
);
|
||||
|
||||
controller.start();
|
||||
},
|
||||
};
|
|
@ -5,7 +5,7 @@ import {
|
|||
verifyValidOption,
|
||||
txSpeeds,
|
||||
customGasChainIds,
|
||||
} from "./utils";
|
||||
} from "../utils";
|
||||
|
||||
type chainMethods = Record<CustomGasChainId, () => Promise<string>>;
|
||||
|
||||
|
@ -32,3 +32,12 @@ export class CustomGasStation {
|
|||
return gweiGasPrice.toString();
|
||||
}
|
||||
}
|
||||
|
||||
export function getCustomGasStation(
|
||||
customGasStation?: number,
|
||||
txSpeed?: string
|
||||
) {
|
||||
if (customGasStation && txSpeed) {
|
||||
return new CustomGasStation(customGasStation, txSpeed);
|
||||
}
|
||||
}
|
|
@ -4,14 +4,14 @@ import {
|
|||
PriceInfo,
|
||||
ChainPriceListener,
|
||||
PriceItem,
|
||||
} from "./interface";
|
||||
} from "../interface";
|
||||
import { TransactionReceipt } from "ethereum-protocol";
|
||||
import { addLeading0x, DurationInSeconds, removeLeading0x } from "./utils";
|
||||
import { addLeading0x, DurationInSeconds, removeLeading0x } from "../utils";
|
||||
import AbstractPythAbi from "@pythnetwork/pyth-sdk-solidity/abis/AbstractPyth.json";
|
||||
import HDWalletProvider from "@truffle/hdwallet-provider";
|
||||
import { Provider } from "web3/providers";
|
||||
import Web3 from "web3";
|
||||
import { isWsEndpoint } from "./utils";
|
||||
import { isWsEndpoint } from "../utils";
|
||||
import {
|
||||
PriceServiceConnection,
|
||||
HexString,
|
|
@ -1,191 +1,14 @@
|
|||
#!/usr/bin/env node
|
||||
// FIXME: refactor this file and command structure
|
||||
// FIXME: update readme and compose files
|
||||
// FIXME: release a new version
|
||||
// #!/usr/bin/env node
|
||||
// // FIXME: update readme and compose files
|
||||
// // FIXME: release a new version
|
||||
import yargs from "yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
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/pyth-common-js";
|
||||
import { InjectivePriceListener, InjectivePricePusher } from "./injective";
|
||||
import { ChainPricePusher, IPriceListener } from "./interface";
|
||||
import { CustomGasStation } from "./custom-gas-station";
|
||||
import injective from "./injective/command";
|
||||
import evm from "./evm/command";
|
||||
|
||||
const argv = yargs(hideBin(process.argv))
|
||||
.option("network", {
|
||||
description: "the blockchain network to push to",
|
||||
type: "string",
|
||||
choices: ["evm", "injective"],
|
||||
required: true,
|
||||
})
|
||||
.option("endpoint", {
|
||||
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 " +
|
||||
"endpoint (`ws[s]://...`), the price pusher will use event subscriptions to read " +
|
||||
"the current EVM price in addition to polling. ",
|
||||
type: "string",
|
||||
required: true,
|
||||
})
|
||||
.option("price-endpoint", {
|
||||
description:
|
||||
"Endpoint URL for the price service. e.g: https://endpoint/example",
|
||||
type: "string",
|
||||
required: true,
|
||||
})
|
||||
.option("pyth-contract", {
|
||||
description:
|
||||
"Pyth contract address. Provide the network name on which Pyth is deployed " +
|
||||
"or the Pyth contract address if you use a local network.",
|
||||
type: "string",
|
||||
required: true,
|
||||
})
|
||||
.option("price-config-file", {
|
||||
description: "Path to price configuration YAML file.",
|
||||
type: "string",
|
||||
required: true,
|
||||
})
|
||||
.option("mnemonic-file", {
|
||||
description: "Path to payer mnemonic (private key) file.",
|
||||
type: "string",
|
||||
required: true,
|
||||
})
|
||||
.option("cooldown-duration", {
|
||||
description:
|
||||
"The amount of time (in seconds) to wait between pushing price updates. " +
|
||||
"This value should be greater than the block time of the network, so this program confirms " +
|
||||
"it is updated and does not push it twice.",
|
||||
type: "number",
|
||||
required: false,
|
||||
default: 10,
|
||||
})
|
||||
.option("polling-frequency", {
|
||||
description:
|
||||
"The frequency to poll price info data from the network if the RPC is not a websocket.",
|
||||
type: "number",
|
||||
required: false,
|
||||
default: 5,
|
||||
})
|
||||
.option("custom-gas-station", {
|
||||
description:
|
||||
"If using a custom gas station, chainId of custom gas station to use",
|
||||
type: "number",
|
||||
required: false,
|
||||
})
|
||||
.option("tx-speed", {
|
||||
description:
|
||||
"txSpeed for custom gas station. choose between 'slow'|'standard'|'fast'",
|
||||
type: "string",
|
||||
required: false,
|
||||
})
|
||||
.help()
|
||||
.alias("help", "h")
|
||||
.parserConfiguration({
|
||||
"parse-numbers": false,
|
||||
})
|
||||
.parseSync();
|
||||
|
||||
const priceConfigs = readPriceConfigFile(argv.priceConfigFile);
|
||||
const priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
|
||||
|
||||
// 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,
|
||||
sourcePriceListener,
|
||||
targetPriceListener,
|
||||
targetPricePusher,
|
||||
{
|
||||
cooldownDuration: argv.cooldownDuration,
|
||||
}
|
||||
);
|
||||
|
||||
await handler.start();
|
||||
}
|
||||
|
||||
const priceServiceConnection = new PriceServiceConnection(argv.priceEndpoint, {
|
||||
logger: console,
|
||||
});
|
||||
|
||||
const pythPriceListener = new PythPriceListener(
|
||||
priceServiceConnection,
|
||||
priceConfigs
|
||||
);
|
||||
|
||||
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, priceItems, {
|
||||
pollingFrequency: argv.pollingFrequency,
|
||||
});
|
||||
}
|
||||
|
||||
case "injective":
|
||||
return new InjectivePriceListener(
|
||||
argv.pythContract,
|
||||
argv.endpoint,
|
||||
priceItems,
|
||||
{ pollingFrequency: argv.pollingFrequency }
|
||||
);
|
||||
default:
|
||||
throw new Error("invalid network");
|
||||
}
|
||||
}
|
||||
|
||||
function getNetworkPricePusher(network: string): ChainPricePusher {
|
||||
const gasStation = getCustomGasStation(argv.customGasStation, argv.txSpeed);
|
||||
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(),
|
||||
gasStation
|
||||
);
|
||||
}
|
||||
case "injective":
|
||||
return new InjectivePricePusher(
|
||||
priceServiceConnection,
|
||||
argv.pythContract,
|
||||
argv.endpoint,
|
||||
fs.readFileSync(argv.mnemonicFile, "utf-8").trim()
|
||||
);
|
||||
default:
|
||||
throw new Error("invalid network");
|
||||
}
|
||||
}
|
||||
|
||||
function getCustomGasStation(customGasStation?: number, txSpeed?: string) {
|
||||
if (customGasStation && txSpeed) {
|
||||
return new CustomGasStation(customGasStation, txSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
start({
|
||||
sourcePriceListener: pythPriceListener,
|
||||
targetPriceListener: getNetworkPriceListener(argv.network),
|
||||
targetPricePusher: getNetworkPricePusher(argv.network),
|
||||
});
|
||||
yargs(hideBin(process.argv))
|
||||
.config("config")
|
||||
.global("config")
|
||||
.command(evm)
|
||||
.command(injective)
|
||||
.help().argv;
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import { PriceServiceConnection } from "@pythnetwork/pyth-common-js";
|
||||
import * as options from "../options";
|
||||
import { readPriceConfigFile } from "../price-config";
|
||||
import fs from "fs";
|
||||
import { InjectivePriceListener, InjectivePricePusher } from "./injective";
|
||||
import { PythPriceListener } from "../pyth-price-listener";
|
||||
import { Controller } from "../controller";
|
||||
import { Options } from "yargs";
|
||||
|
||||
export default {
|
||||
command: "injective",
|
||||
describe: "run price pusher for injective",
|
||||
builder: {
|
||||
"grpc-endpoint": {
|
||||
description:
|
||||
"gRPC endpoint URL for injective. The pusher will periodically" +
|
||||
"poll for updates. The polling interval is configurable via the " +
|
||||
"`polling-frequency` command-line argument.",
|
||||
type: "string",
|
||||
required: true,
|
||||
} as Options,
|
||||
...options.priceConfigFile,
|
||||
...options.priceServiceEndpoint,
|
||||
...options.mnemonicFile,
|
||||
...options.pythContractAddress,
|
||||
...options.pollingFrequency,
|
||||
...options.cooldownDuration,
|
||||
},
|
||||
handler: function (argv: any) {
|
||||
// FIXME: type checks for this
|
||||
const {
|
||||
grpcEndpoint,
|
||||
priceConfigFile,
|
||||
priceServiceEndpoint,
|
||||
mnemonicFile,
|
||||
pythContractAddress,
|
||||
cooldownDuration,
|
||||
pollingFrequency,
|
||||
} = argv;
|
||||
|
||||
const priceConfigs = readPriceConfigFile(priceConfigFile);
|
||||
const priceServiceConnection = new PriceServiceConnection(
|
||||
priceServiceEndpoint,
|
||||
{
|
||||
logger: console,
|
||||
}
|
||||
);
|
||||
const mnemonic = fs.readFileSync(mnemonicFile, "utf-8").trim();
|
||||
|
||||
const priceItems = priceConfigs.map(({ id, alias }) => ({ id, alias }));
|
||||
|
||||
const pythListener = new PythPriceListener(
|
||||
priceServiceConnection,
|
||||
priceConfigs
|
||||
);
|
||||
|
||||
const injectiveListener = new InjectivePriceListener(
|
||||
pythContractAddress,
|
||||
grpcEndpoint,
|
||||
priceItems,
|
||||
{
|
||||
pollingFrequency,
|
||||
}
|
||||
);
|
||||
const injectivePusher = new InjectivePricePusher(
|
||||
priceServiceConnection,
|
||||
pythContractAddress,
|
||||
grpcEndpoint,
|
||||
mnemonic
|
||||
);
|
||||
|
||||
const controller = new Controller(
|
||||
priceConfigs,
|
||||
pythListener,
|
||||
injectiveListener,
|
||||
injectivePusher,
|
||||
{ cooldownDuration }
|
||||
);
|
||||
|
||||
controller.start();
|
||||
},
|
||||
};
|
|
@ -4,8 +4,8 @@ import {
|
|||
PriceInfo,
|
||||
ChainPriceListener,
|
||||
PriceItem,
|
||||
} from "./interface";
|
||||
import { DurationInSeconds } from "./utils";
|
||||
} from "../interface";
|
||||
import { DurationInSeconds } from "../utils";
|
||||
import {
|
||||
ChainGrpcAuthApi,
|
||||
ChainGrpcWasmApi,
|
||||
|
@ -35,6 +35,7 @@ type UpdateFeeResponse = {
|
|||
amount: string;
|
||||
};
|
||||
|
||||
// FIXME: CLEANUP contractAddr variable name consistency
|
||||
// this use price without leading 0x
|
||||
export class InjectivePriceListener extends ChainPriceListener {
|
||||
constructor(
|
|
@ -0,0 +1,58 @@
|
|||
import { Options } from "yargs";
|
||||
|
||||
export const priceServiceEndpoint = {
|
||||
"price-service-endpoint": {
|
||||
description:
|
||||
"Endpoint URL for the price service. e.g: https://endpoint/example",
|
||||
type: "string",
|
||||
required: true,
|
||||
} as Options,
|
||||
};
|
||||
|
||||
export const pythContractAddress = {
|
||||
"pyth-contract-address": {
|
||||
description:
|
||||
"Pyth contract address. Provide the network name on which Pyth is deployed " +
|
||||
"or the Pyth contract address if you use a local network.",
|
||||
type: "string",
|
||||
required: true,
|
||||
} as Options,
|
||||
};
|
||||
|
||||
export const priceConfigFile = {
|
||||
"price-config-file": {
|
||||
description: "Path to price configuration YAML file.",
|
||||
type: "string",
|
||||
required: true,
|
||||
} as Options,
|
||||
};
|
||||
|
||||
export const pollingFrequency = {
|
||||
"polling-frequency": {
|
||||
description:
|
||||
"The frequency to poll price info data from the network if the RPC is not a websocket.",
|
||||
type: "number",
|
||||
required: false,
|
||||
default: 5,
|
||||
} as Options,
|
||||
};
|
||||
|
||||
export const cooldownDuration = {
|
||||
"cooldown-duration": {
|
||||
description:
|
||||
"The amount of time (in seconds) to wait between pushing price updates. " +
|
||||
"This value should be greater than the block time of the network, so this program confirms " +
|
||||
"it is updated and does not push it twice.",
|
||||
type: "number",
|
||||
required: false,
|
||||
default: 10,
|
||||
} as Options,
|
||||
};
|
||||
|
||||
export const mnemonicFile = {
|
||||
"mnemonic-file": {
|
||||
description: "Path to payer mnemonic (private key) file.",
|
||||
type: "string",
|
||||
required: true,
|
||||
} as Options,
|
||||
};
|
Loading…
Reference in New Issue