diff --git a/target_chains/solana/sdk/js/solana_utils/package.json b/target_chains/solana/sdk/js/solana_utils/package.json index bced0514d9..dd1850cc65 100644 --- a/target_chains/solana/sdk/js/solana_utils/package.json +++ b/target_chains/solana/sdk/js/solana_utils/package.json @@ -81,5 +81,5 @@ }, "type": "module", "types": "./dist/cjs/index.d.ts", - "version": "0.6.0" + "version": "0.6.1" } diff --git a/target_chains/solana/sdk/js/solana_utils/src/__tests__/JitoLazyImport.test.ts b/target_chains/solana/sdk/js/solana_utils/src/__tests__/JitoLazyImport.test.ts new file mode 100644 index 0000000000..12ac1762c7 --- /dev/null +++ b/target_chains/solana/sdk/js/solana_utils/src/__tests__/JitoLazyImport.test.ts @@ -0,0 +1,33 @@ +// Regression test for https://github.com/pyth-network/pyth-crosschain/issues/1838. +// +// `jito.ts` historically eager-imported `jito-ts/dist/sdk/block-engine/{searcher,types}`, +// which transitively required `jito-ts`'s nested `@solana/web3.js@~1.77.3` -> +// `rpc-websockets/dist/lib/client` — a path removed in `rpc-websockets@>=7.11`. +// Consumers that loaded any `@pythnetwork/solana-utils` export therefore +// crashed with `Cannot find module 'rpc-websockets/dist/lib/client'`, even +// when they never used the Jito helpers. +// +// We now type-only-import `SearcherClient`/`Bundle` and dynamic-import +// `Bundle` inside `sendTransactionsJito`. This test guards that contract. + +describe("jito-ts lazy loading", () => { + it("does not load jito-ts when importing transaction helpers", async () => { + await import("../transaction"); + + const cachedJitoPath = Object.keys(require.cache).find((p) => + p.includes(`${"/node_modules/"}jito-ts/`), + ); + + expect(cachedJitoPath).toBeUndefined(); + }); + + it("does not load jito-ts when importing jito.ts itself", async () => { + await import("../jito"); + + const cachedJitoPath = Object.keys(require.cache).find((p) => + p.includes(`${"/node_modules/"}jito-ts/`), + ); + + expect(cachedJitoPath).toBeUndefined(); + }); +}); diff --git a/target_chains/solana/sdk/js/solana_utils/src/jito.ts b/target_chains/solana/sdk/js/solana_utils/src/jito.ts index 28d7e6d92d..edcf7dc580 100644 --- a/target_chains/solana/sdk/js/solana_utils/src/jito.ts +++ b/target_chains/solana/sdk/js/solana_utils/src/jito.ts @@ -9,8 +9,13 @@ import type { } from "@solana/web3.js"; import { PublicKey, SystemProgram } from "@solana/web3.js"; import bs58 from "bs58"; +// jito-ts is loaded lazily inside `sendTransactionsJito` so that consumers +// who only use the non-Jito transaction helpers do not pay the cost of +// jito-ts pulling its nested `@solana/web3.js@~1.77.3` (which transitively +// `require`s a rpc-websockets path removed in rpc-websockets@>=7.11). See +// https://github.com/pyth-network/pyth-crosschain/issues/1838. import type { SearcherClient } from "jito-ts/dist/sdk/block-engine/searcher"; -import { Bundle } from "jito-ts/dist/sdk/block-engine/types"; +import type { Bundle as BundleType } from "jito-ts/dist/sdk/block-engine/types"; import type { Logger } from "ts-log"; import { dummyLogger } from "ts-log"; @@ -85,7 +90,10 @@ export async function sendTransactionsJito( signedTransactions[0]?.signatures[0]!, ); - const bundle = new Bundle(signedTransactions, 2); + // Dynamic import so jito-ts (and its old @solana/web3.js transitive dep) + // is only resolved when this code path is actually taken. + const { Bundle } = await import("jito-ts/dist/sdk/block-engine/types"); + const bundle: BundleType = new Bundle(signedTransactions, 2); let lastError: Error | null | undefined; let totalAttempts = 0;