From b3c697e5f59c28e1a4844b42d36dba0480cedee0 Mon Sep 17 00:00:00 2001 From: Maurechi <71376233+Maurechi@users.noreply.github.com> Date: Tue, 28 Feb 2023 21:58:48 +0100 Subject: [PATCH] added customGasStation (#644) --- price_pusher/src/custom-gas-station.ts | 34 ++++++++++++++++++++++++++ price_pusher/src/evm.ts | 14 ++++++++--- price_pusher/src/index.ts | 23 ++++++++++++++++- price_pusher/src/utils.ts | 16 ++++++++++++ 4 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 price_pusher/src/custom-gas-station.ts diff --git a/price_pusher/src/custom-gas-station.ts b/price_pusher/src/custom-gas-station.ts new file mode 100644 index 00000000..93d551c2 --- /dev/null +++ b/price_pusher/src/custom-gas-station.ts @@ -0,0 +1,34 @@ +import Web3 from "web3"; +import { + CustomGasChainId, + TxSpeed, + verifyValidOption, + txSpeeds, + customGasChainIds, +} from "./utils"; + +type chainMethods = Record Promise>; + +export class CustomGasStation { + private chain: CustomGasChainId; + private speed: TxSpeed; + private chainMethods: chainMethods = { + 137: this.fetchMaticMainnetGasPrice.bind(this), + }; + constructor(chain: number, speed: string) { + this.speed = verifyValidOption(speed, txSpeeds); + this.chain = verifyValidOption(chain, customGasChainIds); + } + + async getCustomGasPrice() { + return this.chainMethods[this.chain](); + } + + private async fetchMaticMainnetGasPrice() { + const res = await fetch("https://gasstation-mainnet.matic.network/v2"); + const jsonRes = await res.json(); + const gasPrice = jsonRes[this.speed].maxFee; + const gweiGasPrice = Web3.utils.toWei(gasPrice.toFixed(2), "Gwei"); + return gweiGasPrice.toString(); + } +} diff --git a/price_pusher/src/evm.ts b/price_pusher/src/evm.ts index abb62986..a9fdcc2e 100644 --- a/price_pusher/src/evm.ts +++ b/price_pusher/src/evm.ts @@ -17,6 +17,7 @@ import { HexString, UnixTimestamp, } from "@pythnetwork/pyth-common-js"; +import { CustomGasStation } from "./custom-gas-station"; export class EvmPriceListener extends ChainPriceListener { private pythContractFactory: PythContractFactory; @@ -117,10 +118,14 @@ export class EvmPriceListener extends ChainPriceListener { } export class EvmPricePusher implements ChainPricePusher { + private customGasStation?: CustomGasStation; constructor( private connection: PriceServiceConnection, - private pythContract: Contract - ) {} + private pythContract: Contract, + customGasStation?: CustomGasStation + ) { + this.customGasStation = customGasStation; + } // The pubTimes are passed here to use the values that triggered the push. // This is an optimization to avoid getting a newer value (as an update comes) // and will help multiple price pushers to have consistent behaviour. @@ -145,19 +150,20 @@ export class EvmPricePusher implements ChainPricePusher { "Pushing ", priceIdsWith0x.map((priceIdWith0x) => `${priceIdWith0x}`) ); - const updateFee = await this.pythContract.methods .getUpdateFee(priceFeedUpdateData) .call(); console.log(`Update fee: ${updateFee}`); + const gasPrice = await this.customGasStation?.getCustomGasPrice(); + this.pythContract.methods .updatePriceFeedsIfNecessary( priceFeedUpdateData, priceIdsWith0x, pubTimesToPush ) - .send({ value: updateFee }) + .send({ value: updateFee, gasPrice }) .on("transactionHash", (hash: string) => { console.log(`Successful. Tx hash: ${hash}`); }) diff --git a/price_pusher/src/index.ts b/price_pusher/src/index.ts index d989da4c..274ada24 100644 --- a/price_pusher/src/index.ts +++ b/price_pusher/src/index.ts @@ -12,6 +12,7 @@ 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"; const argv = yargs(hideBin(process.argv)) .option("network", { @@ -69,6 +70,18 @@ const argv = yargs(hideBin(process.argv)) 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({ @@ -139,6 +152,7 @@ function getNetworkPriceListener(network: string): IPriceListener { } function getNetworkPricePusher(network: string): ChainPricePusher { + const gasStation = getCustomGasStation(argv.customGasStation, argv.txSpeed); switch (network) { case "evm": { const pythContractFactory = new PythContractFactory( @@ -148,7 +162,8 @@ function getNetworkPricePusher(network: string): ChainPricePusher { ); return new EvmPricePusher( priceServiceConnection, - pythContractFactory.createPythContractWithPayer() + pythContractFactory.createPythContractWithPayer(), + gasStation ); } case "injective": @@ -163,6 +178,12 @@ function getNetworkPricePusher(network: string): ChainPricePusher { } } +function getCustomGasStation(customGasStation?: number, txSpeed?: string) { + if (customGasStation && txSpeed) { + return new CustomGasStation(customGasStation, txSpeed); + } +} + start({ sourcePriceListener: pythPriceListener, targetPriceListener: getNetworkPriceListener(argv.network), diff --git a/price_pusher/src/utils.ts b/price_pusher/src/utils.ts index 69b39470..685667eb 100644 --- a/price_pusher/src/utils.ts +++ b/price_pusher/src/utils.ts @@ -2,6 +2,10 @@ import { HexString } from "@pythnetwork/pyth-common-js"; export type PctNumber = number; export type DurationInSeconds = number; +export const txSpeeds = ["slow", "standard", "fast"] as const; +export type TxSpeed = typeof txSpeeds[number]; +export const customGasChainIds = [137] as const; +export type CustomGasChainId = typeof customGasChainIds[number]; export async function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); @@ -31,3 +35,15 @@ export function isWsEndpoint(endpoint: string): boolean { return false; } + +export function verifyValidOption< + options extends Readonly>, + validOption extends options[number] +>(option: any, validOptions: options) { + if (validOptions.includes(option)) { + return option as validOption; + } + const errorString = + option + " is not a valid option. Please choose between " + validOptions; + throw new Error(errorString); +}