Skip to content

Commit 5e45b52

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 75472c0 commit 5e45b52

3 files changed

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

@@ -85,7 +90,10 @@ export async function sendTransactionsJito(
8590
signedTransactions[0]?.signatures[0]!,
8691
);
8792

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

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

0 commit comments

Comments
 (0)