diff --git a/apps/price_pusher/src/evm/command.ts b/apps/price_pusher/src/evm/command.ts index c965e7b6ad..f2d0525e59 100644 --- a/apps/price_pusher/src/evm/command.ts +++ b/apps/price_pusher/src/evm/command.ts @@ -68,6 +68,12 @@ export default { type: "number", required: false, } as Options, + "use-recent-gas-price-estimate": { + description: "Use gas price based on recent blocks", + type: "boolean", + required: false, + default: false, + } as Options, "update-fee-multiplier": { description: "Multiplier for the fee to update the price. It is useful in networks " + @@ -77,6 +83,12 @@ export default { required: false, default: 1, } as Options, + "disable-push": { + description: "Dry run without pushing", + type: "boolean", + required: false, + default: false, + } as Options, ...options.priceConfigFile, ...options.priceServiceEndpoint, ...options.mnemonicFile, @@ -104,11 +116,13 @@ export default { overrideGasPriceMultiplierCap, gasLimit, gasPrice, + useRecentGasPriceEstimate, updateFeeMultiplier, logLevel, controllerLogLevel, enableMetrics, metricsPort, + disablePush, } = argv; const logger = pino({ @@ -151,6 +165,7 @@ export default { ); const client = await createClient(endpoint, mnemonic); + const network = await client.getChainId().then((id) => id.toString()); const pythContract = createPythContract(client, pythContractAddress); logger.info( @@ -185,6 +200,9 @@ export default { overrideGasPriceMultiplier, overrideGasPriceMultiplierCap, updateFeeMultiplier, + network, + disablePush, + useRecentGasPriceEstimate, gasLimit, gasStation, gasPrice, @@ -207,7 +225,7 @@ export default { const balanceTracker = createEvmBalanceTracker({ client, address: client.account.address, - network: await client.getChainId().then((id) => id.toString()), + network, updateInterval: pushingFrequency, metrics, logger, diff --git a/apps/price_pusher/src/evm/evm.ts b/apps/price_pusher/src/evm/evm.ts index 8dbf9ab05f..4682604878 100644 --- a/apps/price_pusher/src/evm/evm.ts +++ b/apps/price_pusher/src/evm/evm.ts @@ -32,6 +32,7 @@ import { import { PythContract } from "./pyth-contract"; import { SuperWalletClient } from "./super-wallet"; +import { PricePusherMetrics } from "../metrics"; export class EvmPriceListener extends ChainPriceListener { constructor( @@ -135,9 +136,13 @@ export class EvmPricePusher implements IPricePusher { private overrideGasPriceMultiplier: number, private overrideGasPriceMultiplierCap: number, private updateFeeMultiplier: number, + private network: string, + private disablePush: boolean, + private useRecentGasPriceEstimate: boolean, private gasLimit?: number, private customGasStation?: CustomGasStation, private gasPrice?: number, + private metrics?: PricePusherMetrics, ) {} // The pubTimes are passed here to use the values that triggered the push. @@ -191,8 +196,11 @@ export class EvmPricePusher implements IPricePusher { this.gasPrice ?? Number( await (this.customGasStation?.getCustomGasPrice() ?? - this.client.getGasPrice()), + (this.useRecentGasPriceEstimate + ? this.getMedianRecentGasPrice() + : this.client.getGasPrice())), ); + this.metrics?.updateGasPrice(this.network, gasPrice); // Try to re-use the same nonce and increase the gas if the last tx is not landed yet. if (this.pusherAddress === undefined) { @@ -258,11 +266,13 @@ export class EvmPricePusher implements IPricePusher { this.logger.debug({ request }, "Simulated request successfully"); - const hash = await this.client.writeContract(request); - - this.logger.info({ hash }, "Price update sent"); - - this.waitForTransactionReceipt(hash); + if (!this.disablePush) { + const hash = await this.client.writeContract(request); + this.logger.info({ hash }, "Price update sent"); + this.waitForTransactionReceipt(hash); + } else { + this.logger.debug("Push disabled, not attempting"); + } } catch (err: any) { this.logger.debug({ err }, "Simulating or sending transactions failed."); @@ -423,4 +433,33 @@ export class EvmPricePusher implements IPricePusher { }); return response.binary.data; } + + async getMedianRecentGasPrice(blockCount = 5): Promise { + this.logger.info({ blockCount }); + const { baseFeePerGas, reward } = await this.client.getFeeHistory({ + blockCount, + rewardPercentiles: [50], + blockTag: "latest", + }); + this.logger.info({ baseFeePerGas, reward }, "feeHistory"); + // remove the next block base fee + const trimmedBaseFees = baseFeePerGas.slice(0, -1); + const gasPrices = trimmedBaseFees.map((base, i) => { + const medianTip = reward?.[i]?.[0] ?? 0n; + return base + medianTip; + }); + this.logger.info({ gasPrices }, "gasPrices:"); + + if (gasPrices.length === 0) { + return 0n; + } else { + const sorted = [...gasPrices].sort((a, b) => + a < b ? -1 : a > b ? 1 : 0, + ); + const medianIndex = Math.floor(sorted.length / 2); + const medianPrice = sorted[medianIndex]; + this.logger.info({ medianPrice }, "medianPrice:"); + return medianPrice; + } + } } diff --git a/apps/price_pusher/src/metrics.ts b/apps/price_pusher/src/metrics.ts index abea497d3a..8bfe5658f5 100644 --- a/apps/price_pusher/src/metrics.ts +++ b/apps/price_pusher/src/metrics.ts @@ -19,6 +19,8 @@ export class PricePusherMetrics { public targetPriceValue: Gauge; // Wallet metrics public walletBalance: Gauge; + // gas price + public gasPrice: Gauge; constructor(logger: Logger) { this.logger = logger; @@ -85,6 +87,13 @@ export class PricePusherMetrics { registers: [this.registry], }); + this.gasPrice = new Gauge({ + name: "pyth_gas_price", + help: "Gas price estimate for this chain", + labelNames: ["network"], + registers: [this.registry], + }); + // Setup the metrics endpoint this.server.get("/metrics", async (req, res) => { res.set("Content-Type", this.registry.contentType); @@ -212,4 +221,12 @@ export class PricePusherMetrics { `Updated wallet balance metric: ${walletAddress} = ${balanceNum}`, ); } + + public updateGasPrice(network: string, gasPrice: bigint | number): void { + // Convert to number for compatibility with prometheus + const gasPriceNum = + typeof gasPrice === "bigint" ? Number(gasPrice) : gasPrice; + this.gasPrice.set({ network }, gasPriceNum); + this.logger.debug(`Updated gas price metric: ${network} = ${gasPriceNum}`); + } }