Skip to content

Commit 1fd6037

Browse files
committed
fix(solana-utils): lazy-load jito-ts so non-Jito consumers don't crash
Closes #1838. `jito.ts` previously did top-level value imports of `jito-ts/dist/sdk/block-engine/{searcher,types}`. Pulling those modules also pulls jito-ts's nested `@solana/web3.js@~1.77.3`, which transitively `require`s `rpc-websockets/dist/lib/client` — a path removed in `rpc-websockets@>=7.11`. Because `solana_utils/transaction.ts` imports `buildJitoTipInstruction` from `./jito`, any consumer that loaded a @pythnetwork/solana-utils export — including `PythSolanaReceiver` from `@pythnetwork/pyth-solana-receiver` — eagerly walked the broken chain and crashed at module load with: Error: Cannot find module 'rpc-websockets/dist/lib/client' This change splits the imports so that jito-ts only resolves when the Jito send path is actually exercised: - `SearcherClient` and `Bundle` become `import type` — these were only used as types in function signatures. - `sendTransactionsJito` dynamic-`import`s `Bundle` from `jito-ts/.../types` immediately before constructing the bundle. The function is already async, so this is zero-cost for callers. Non-Jito consumers (the case reported in the issue) no longer trigger the broken require. Jito users still get the same runtime path; the underlying jito-ts/rpc-websockets clash there is a separate problem for jito-ts to solve. Tests - Added `JitoLazyImport.test.ts` asserting that importing either `../transaction` or `../jito` leaves `jito-ts/*` out of `require.cache`. - Existing `TransactionSize.test.ts` still passes (2 cases). - `pnpm --filter @pythnetwork/solana-utils build` succeeds (esm + cjs). Bumped to 0.6.1 (patch — runtime contract preserved, only load order changes).
1 parent 5c32765 commit 1fd6037

3 files changed

Lines changed: 45 additions & 4 deletions

File tree

target_chains/solana/sdk/js/solana_utils/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,5 @@
8181
},
8282
"type": "module",
8383
"types": "./dist/cjs/index.d.ts",
84-
"version": "0.6.0"
84+
"version": "0.6.1"
8585
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Regression test for https://github.com/pyth-network/pyth-crosschain/issues/1838.
2+
//
3+
// `jito.ts` historically eager-imported `jito-ts/dist/sdk/block-engine/{searcher,types}`,
4+
// which transitively required `jito-ts`'s nested `@solana/web3.js@~1.77.3` ->
5+
// `rpc-websockets/dist/lib/client` — a path removed in `rpc-websockets@>=7.11`.
6+
// Consumers that loaded any `@pythnetwork/solana-utils` export therefore
7+
// crashed with `Cannot find module 'rpc-websockets/dist/lib/client'`, even
8+
// when they never used the Jito helpers.
9+
//
10+
// We now type-only-import `SearcherClient`/`Bundle` and dynamic-import
11+
// `Bundle` inside `sendTransactionsJito`. This test guards that contract.
12+
13+
describe("jito-ts lazy loading", () => {
14+
it("does not load jito-ts when importing transaction helpers", async () => {
15+
await import("../transaction");
16+
17+
const cachedJitoPath = Object.keys(require.cache).find((p) =>
18+
p.includes(`${"/node_modules/"}jito-ts/`),
19+
);
20+
21+
expect(cachedJitoPath).toBeUndefined();
22+
});
23+
24+
it("does not load jito-ts when importing jito.ts itself", async () => {
25+
await import("../jito");
26+
27+
const cachedJitoPath = Object.keys(require.cache).find((p) =>
28+
p.includes(`${"/node_modules/"}jito-ts/`),
29+
);
30+
31+
expect(cachedJitoPath).toBeUndefined();
32+
});
33+
});

target_chains/solana/sdk/js/solana_utils/src/jito.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ import {
1010
VersionedTransaction,
1111
} from "@solana/web3.js";
1212
import bs58 from "bs58";
13-
import { SearcherClient } from "jito-ts/dist/sdk/block-engine/searcher";
14-
import { Bundle } from "jito-ts/dist/sdk/block-engine/types";
13+
// jito-ts is loaded lazily inside `sendTransactionsJito` so that consumers
14+
// who only use the non-Jito transaction helpers do not pay the cost of
15+
// jito-ts pulling its nested `@solana/web3.js@~1.77.3` (which transitively
16+
// `require`s a rpc-websockets path removed in rpc-websockets@>=7.11). See
17+
// https://github.com/pyth-network/pyth-crosschain/issues/1838.
18+
import type { SearcherClient } from "jito-ts/dist/sdk/block-engine/searcher";
19+
import type { Bundle as BundleType } from "jito-ts/dist/sdk/block-engine/types";
1520
import type { Logger } from "ts-log";
1621
import { dummyLogger } from "ts-log";
1722

@@ -86,7 +91,10 @@ export async function sendTransactionsJito(
8691
signedTransactions[0]?.signatures[0]!,
8792
);
8893

89-
const bundle = new Bundle(signedTransactions, 2);
94+
// Dynamic import so jito-ts (and its old @solana/web3.js transitive dep)
95+
// is only resolved when this code path is actually taken.
96+
const { Bundle } = await import("jito-ts/dist/sdk/block-engine/types");
97+
const bundle: BundleType = new Bundle(signedTransactions, 2);
9098

9199
let lastError: Error | null | undefined;
92100
let totalAttempts = 0;

0 commit comments

Comments
 (0)