Skip to content

Commit c6ec886

Browse files
authored
feat: add dynamic jito tips (#2113)
* add dynamic jito tips * uncomment * go * using jito tip * go * bump * cap it * go * go * test * disable health check * go
1 parent 0051203 commit c6ec886

File tree

3 files changed

+67
-18
lines changed

3 files changed

+67
-18
lines changed

apps/price_pusher/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/price-pusher",
3-
"version": "8.1.0",
3+
"version": "8.2.0",
44
"description": "Pyth Price Pusher",
55
"homepage": "https://pyth.network",
66
"main": "lib/index.js",

apps/price_pusher/src/solana/command.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import { Controller } from "../controller";
1212
import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver";
1313
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
14-
import { Keypair, Connection } from "@solana/web3.js";
14+
import { Keypair, Connection, LAMPORTS_PER_SOL } from "@solana/web3.js";
1515
import fs from "fs";
1616
import { PublicKey } from "@solana/web3.js";
1717
import {
@@ -61,6 +61,16 @@ export default {
6161
type: "number",
6262
optional: true,
6363
} as Options,
64+
"dynamic-jito-tips": {
65+
description: "Use dynamic jito tips",
66+
type: "boolean",
67+
default: false,
68+
} as Options,
69+
"max-jito-tip-lamports": {
70+
description: "Maximum jito tip lamports",
71+
type: "number",
72+
default: LAMPORTS_PER_SOL / 100,
73+
} as Options,
6474
"jito-bundle-size": {
6575
description: "Number of transactions in each bundle",
6676
type: "number",
@@ -94,6 +104,8 @@ export default {
94104
jitoEndpoint,
95105
jitoKeypairFile,
96106
jitoTipLamports,
107+
dynamicJitoTips,
108+
maxJitoTipLamports,
97109
jitoBundleSize,
98110
updatesPerJitoBundle,
99111
logLevel,
@@ -148,6 +160,8 @@ export default {
148160
logger.child({ module: "SolanaPricePusherJito" }),
149161
shardId,
150162
jitoTipLamports,
163+
dynamicJitoTips,
164+
maxJitoTipLamports,
151165
jitoClient,
152166
jitoBundleSize,
153167
updatesPerJitoBundle

apps/price_pusher/src/solana/solana.ts

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { SearcherClient } from "jito-ts/dist/sdk/block-engine/searcher";
1515
import { sliceAccumulatorUpdateData } from "@pythnetwork/price-service-sdk";
1616
import { Logger } from "pino";
17+
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
1718

1819
const HEALTH_CHECK_TIMEOUT_SECONDS = 60;
1920

@@ -34,21 +35,24 @@ export class SolanaPriceListener extends ChainPriceListener {
3435
// and ensuring it is not older than 30 seconds.
3536
private async checkHealth() {
3637
const slot = await this.pythSolanaReceiver.connection.getSlot("finalized");
37-
const blockTime = await this.pythSolanaReceiver.connection.getBlockTime(
38-
slot
39-
);
40-
if (
41-
blockTime === null ||
42-
blockTime < Date.now() / 1000 - HEALTH_CHECK_TIMEOUT_SECONDS
43-
) {
44-
if (blockTime !== null) {
45-
this.logger.info(
46-
`Solana connection is behind by ${
47-
Date.now() / 1000 - blockTime
48-
} seconds`
49-
);
38+
try {
39+
const blockTime = await this.pythSolanaReceiver.connection.getBlockTime(
40+
slot
41+
);
42+
if (
43+
blockTime === null ||
44+
blockTime < Date.now() / 1000 - HEALTH_CHECK_TIMEOUT_SECONDS
45+
) {
46+
if (blockTime !== null) {
47+
this.logger.info(
48+
`Solana connection is behind by ${
49+
Date.now() / 1000 - blockTime
50+
} seconds`
51+
);
52+
}
5053
}
51-
throw new Error("Solana connection is unhealthy");
54+
} catch (err) {
55+
this.logger.error({ err }, "checkHealth failed");
5256
}
5357
}
5458

@@ -155,17 +159,48 @@ export class SolanaPricePusherJito implements IPricePusher {
155159
private priceServiceConnection: PriceServiceConnection,
156160
private logger: Logger,
157161
private shardId: number,
158-
private jitoTipLamports: number,
162+
private defaultJitoTipLamports: number,
163+
private dynamicJitoTips: boolean,
164+
private maxJitoTipLamports: number,
159165
private searcherClient: SearcherClient,
160166
private jitoBundleSize: number,
161167
private updatesPerJitoBundle: number
162168
) {}
163169

170+
async getRecentJitoTipLamports(): Promise<number | undefined> {
171+
try {
172+
const response = await fetch(
173+
"http://bundles-api-rest.jito.wtf/api/v1/bundles/tip_floor"
174+
);
175+
if (!response.ok) {
176+
this.logger.error(
177+
{ status: response.status, statusText: response.statusText },
178+
"getRecentJitoTips http request failed"
179+
);
180+
return undefined;
181+
}
182+
const data = await response.json();
183+
return Math.floor(
184+
Number(data[0].landed_tips_25th_percentile) * LAMPORTS_PER_SOL
185+
);
186+
} catch (err: any) {
187+
this.logger.error({ err }, "getRecentJitoTips failed");
188+
return undefined;
189+
}
190+
}
191+
164192
async updatePriceFeed(
165193
priceIds: string[],
166194
// eslint-disable-next-line @typescript-eslint/no-unused-vars
167195
_pubTimesToPush: number[]
168196
): Promise<void> {
197+
const jitoTip = this.dynamicJitoTips
198+
? (await this.getRecentJitoTipLamports()) ?? this.defaultJitoTipLamports
199+
: this.defaultJitoTipLamports;
200+
201+
const cappedJitoTip = Math.min(jitoTip, this.maxJitoTipLamports);
202+
this.logger.info({ cappedJitoTip }, "using jito tip of");
203+
169204
let priceFeedUpdateData: string[];
170205
try {
171206
priceFeedUpdateData = await this.priceServiceConnection.getLatestVaas(
@@ -192,7 +227,7 @@ export class SolanaPricePusherJito implements IPricePusher {
192227
);
193228

194229
const transactions = await transactionBuilder.buildVersionedTransactions({
195-
jitoTipLamports: this.jitoTipLamports,
230+
jitoTipLamports: cappedJitoTip,
196231
tightComputeBudget: true,
197232
jitoBundleSize: this.jitoBundleSize,
198233
});

0 commit comments

Comments
 (0)