Skip to content

Commit 9e422a6

Browse files
committed
feat(price_pusher): Optionally use median of recent gas prices
1 parent b914d46 commit 9e422a6

File tree

3 files changed

+86
-7
lines changed

3 files changed

+86
-7
lines changed

apps/price_pusher/src/evm/command.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ export default {
6868
type: "number",
6969
required: false,
7070
} as Options,
71+
"use-recent-gas-price-estimate": {
72+
description: "Use gas price based on recent blocks",
73+
type: "boolean",
74+
required: false,
75+
default: false,
76+
} as Options,
7177
"update-fee-multiplier": {
7278
description:
7379
"Multiplier for the fee to update the price. It is useful in networks " +
@@ -76,6 +82,11 @@ export default {
7682
type: "number",
7783
required: false,
7884
default: 1,
85+
} as Options, "disable-push": {
86+
description: "Dry run without pushing",
87+
type: "boolean",
88+
required: false,
89+
default: false,
7990
} as Options,
8091
...options.priceConfigFile,
8192
...options.priceServiceEndpoint,
@@ -104,11 +115,13 @@ export default {
104115
overrideGasPriceMultiplierCap,
105116
gasLimit,
106117
gasPrice,
118+
useRecentGasPriceEstimate,
107119
updateFeeMultiplier,
108120
logLevel,
109121
controllerLogLevel,
110122
enableMetrics,
111123
metricsPort,
124+
disablePush,
112125
} = argv;
113126

114127
const logger = pino({
@@ -151,6 +164,7 @@ export default {
151164
);
152165

153166
const client = await createClient(endpoint, mnemonic);
167+
const network = await client.getChainId().then((id) => id.toString());
154168
const pythContract = createPythContract(client, pythContractAddress);
155169

156170
logger.info(
@@ -185,6 +199,9 @@ export default {
185199
overrideGasPriceMultiplier,
186200
overrideGasPriceMultiplierCap,
187201
updateFeeMultiplier,
202+
network,
203+
disablePush,
204+
useRecentGasPriceEstimate,
188205
gasLimit,
189206
gasStation,
190207
gasPrice,
@@ -207,7 +224,7 @@ export default {
207224
const balanceTracker = createEvmBalanceTracker({
208225
client,
209226
address: client.account.address,
210-
network: await client.getChainId().then((id) => id.toString()),
227+
network,
211228
updateInterval: pushingFrequency,
212229
metrics,
213230
logger,

apps/price_pusher/src/evm/evm.ts

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232

3333
import { PythContract } from "./pyth-contract";
3434
import { SuperWalletClient } from "./super-wallet";
35+
import { PricePusherMetrics } from "../metrics";
3536

3637
export class EvmPriceListener extends ChainPriceListener {
3738
constructor(
@@ -135,9 +136,13 @@ export class EvmPricePusher implements IPricePusher {
135136
private overrideGasPriceMultiplier: number,
136137
private overrideGasPriceMultiplierCap: number,
137138
private updateFeeMultiplier: number,
139+
private network: string,
140+
private disablePush: boolean,
141+
private useRecentGasPriceEstimate: boolean,
138142
private gasLimit?: number,
139143
private customGasStation?: CustomGasStation,
140144
private gasPrice?: number,
145+
private metrics?: PricePusherMetrics,
141146
) {}
142147

143148
// The pubTimes are passed here to use the values that triggered the push.
@@ -191,8 +196,11 @@ export class EvmPricePusher implements IPricePusher {
191196
this.gasPrice ??
192197
Number(
193198
await (this.customGasStation?.getCustomGasPrice() ??
194-
this.client.getGasPrice()),
199+
(this.useRecentGasPriceEstimate
200+
? this.getMedianRecentGasPrice()
201+
: this.client.getGasPrice())),
195202
);
203+
this.metrics?.updateGasPrice(this.network, gasPrice);
196204

197205
// Try to re-use the same nonce and increase the gas if the last tx is not landed yet.
198206
if (this.pusherAddress === undefined) {
@@ -258,11 +266,13 @@ export class EvmPricePusher implements IPricePusher {
258266

259267
this.logger.debug({ request }, "Simulated request successfully");
260268

261-
const hash = await this.client.writeContract(request);
262-
263-
this.logger.info({ hash }, "Price update sent");
264-
265-
this.waitForTransactionReceipt(hash);
269+
if (!this.disablePush) {
270+
const hash = await this.client.writeContract(request);
271+
this.logger.info({ hash }, "Price update sent");
272+
this.waitForTransactionReceipt(hash);
273+
} else {
274+
this.logger.debug("Push disabled, not attempting");
275+
}
266276
} catch (err: any) {
267277
this.logger.debug({ err }, "Simulating or sending transactions failed.");
268278

@@ -423,4 +433,31 @@ export class EvmPricePusher implements IPricePusher {
423433
});
424434
return response.binary.data;
425435
}
436+
437+
async getMedianRecentGasPrice(blockCount = 5): Promise<bigint> {
438+
this.logger.info({ blockCount });
439+
const { baseFeePerGas, reward } = await this.client.getFeeHistory({
440+
blockCount,
441+
rewardPercentiles: [50],
442+
blockTag: "latest",
443+
});
444+
this.logger.info({ baseFeePerGas, reward }, "feeHistory");
445+
// remove the next block base fee
446+
const trimmedBaseFees = baseFeePerGas.slice(0, -1)
447+
const gasPrices = trimmedBaseFees.map((base, i) => {
448+
const medianTip = reward?.[i]?.[0] ?? 0n;
449+
return base + medianTip;
450+
})
451+
this.logger.info({gasPrices}, "gasPrices:");
452+
453+
if (gasPrices.length === 0) {
454+
return 0n;
455+
} else {
456+
const sorted = [...gasPrices].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
457+
const medianIndex = Math.floor(sorted.length / 2)
458+
const medianPrice = sorted[medianIndex];
459+
this.logger.info({ medianPrice }, "medianPrice:");
460+
return medianPrice;
461+
}
462+
}
426463
}

apps/price_pusher/src/metrics.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export class PricePusherMetrics {
1919
public targetPriceValue: Gauge<string>;
2020
// Wallet metrics
2121
public walletBalance: Gauge<string>;
22+
// gas price
23+
public gasPrice: Gauge<string>;
2224

2325
constructor(logger: Logger) {
2426
this.logger = logger;
@@ -85,6 +87,13 @@ export class PricePusherMetrics {
8587
registers: [this.registry],
8688
});
8789

90+
this.gasPrice = new Gauge({
91+
name: "pyth_gas_price",
92+
help: "Gas price estimate for this chain",
93+
labelNames: ["network"],
94+
registers: [this.registry],
95+
});
96+
8897
// Setup the metrics endpoint
8998
this.server.get("/metrics", async (req, res) => {
9099
res.set("Content-Type", this.registry.contentType);
@@ -212,4 +221,20 @@ export class PricePusherMetrics {
212221
`Updated wallet balance metric: ${walletAddress} = ${balanceNum}`,
213222
);
214223
}
224+
225+
public updateGasPrice(
226+
network: string,
227+
gasPrice: bigint | number,
228+
): void {
229+
// Convert to number for compatibility with prometheus
230+
const gasPriceNum =
231+
typeof gasPrice === "bigint" ? Number(gasPrice) : gasPrice;
232+
this.gasPrice.set(
233+
{ network },
234+
gasPriceNum,
235+
);
236+
this.logger.debug(
237+
`Updated gas price metric: ${network} = ${gasPriceNum}`,
238+
);
239+
}
215240
}

0 commit comments

Comments
 (0)