Skip to content

Commit 689e557

Browse files
committed
feat(rpc): implement RPC URL override functionality in various modules
1 parent 9b94223 commit 689e557

File tree

7 files changed

+207
-11
lines changed

7 files changed

+207
-11
lines changed

e2e/src/e2e.spec.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ import {
1919
} from './helper/tests';
2020
import { init } from './init';
2121

22+
const RPC_OVERRIDE = process.env['LIT_YELLOWSTONE_PRIVATE_RPC_URL'];
23+
if (RPC_OVERRIDE) {
24+
console.log(
25+
'🧪 E2E: Using RPC override (LIT_YELLOWSTONE_PRIVATE_RPC_URL):',
26+
RPC_OVERRIDE
27+
);
28+
}
29+
2230
describe('all', () => {
2331
// Singleton baby
2432
let ctx: Awaited<ReturnType<typeof init>>;
@@ -161,3 +169,99 @@ describe('all', () => {
161169
});
162170
});
163171
});
172+
173+
describe('rpc override', () => {
174+
const ORIGINAL_RPC = process.env.LIT_YELLOWSTONE_PRIVATE_RPC_URL;
175+
const TEST_RPC = 'https://yellowstone-override.example';
176+
177+
beforeAll(() => {
178+
process.env.LIT_YELLOWSTONE_PRIVATE_RPC_URL = TEST_RPC;
179+
});
180+
181+
afterAll(() => {
182+
process.env.LIT_YELLOWSTONE_PRIVATE_RPC_URL = ORIGINAL_RPC;
183+
});
184+
185+
it('applies env rpc override to module and client', async () => {
186+
const networks = await import('@lit-protocol/networks');
187+
188+
// choose module by NETWORK env (same way init.ts does)
189+
const network = process.env.NETWORK || 'naga-dev';
190+
const importNameMap: Record<string, string> = {
191+
'naga-dev': 'nagaDev',
192+
'naga-test': 'nagaTest',
193+
'naga-local': 'nagaLocal',
194+
'naga-staging': 'nagaStaging',
195+
};
196+
const importName = importNameMap[network];
197+
const baseModule: any = (networks as any)[importName];
198+
199+
// apply override
200+
const mod =
201+
typeof baseModule.withOverrides === 'function'
202+
? baseModule.withOverrides({ rpcUrl: TEST_RPC })
203+
: baseModule;
204+
205+
// log for verification
206+
// base vs effective (when override is supported)
207+
const baseRpcUrl =
208+
typeof baseModule.getRpcUrl === 'function' ? baseModule.getRpcUrl() : 'n/a';
209+
const effRpcUrl =
210+
typeof mod.getRpcUrl === 'function' ? mod.getRpcUrl() : 'n/a';
211+
// eslint-disable-next-line no-console
212+
console.log('[rpc-override] TEST_RPC:', TEST_RPC);
213+
// eslint-disable-next-line no-console
214+
console.log('[rpc-override] module rpc (base → effective):', baseRpcUrl, '→', effRpcUrl);
215+
try {
216+
const baseChain =
217+
typeof baseModule.getChainConfig === 'function' ? baseModule.getChainConfig() : null;
218+
const effChain =
219+
typeof mod.getChainConfig === 'function' ? mod.getChainConfig() : null;
220+
if (baseChain && effChain) {
221+
// eslint-disable-next-line no-console
222+
console.log(
223+
'[rpc-override] module chain id/name (base → effective):',
224+
`${baseChain.id}/${baseChain.name}`,
225+
'→',
226+
`${effChain.id}/${effChain.name}`
227+
);
228+
// eslint-disable-next-line no-console
229+
console.log(
230+
'[rpc-override] module rpcUrls.default.http (base → effective):',
231+
baseChain.rpcUrls.default.http,
232+
'→',
233+
effChain.rpcUrls.default.http
234+
);
235+
// eslint-disable-next-line no-console
236+
console.log(
237+
'[rpc-override] module rpcUrls.public.http (base → effective):',
238+
(baseChain.rpcUrls as any)['public']?.http,
239+
'→',
240+
(effChain.rpcUrls as any)['public']?.http
241+
);
242+
}
243+
} catch {}
244+
245+
// module reflects override
246+
expect(mod.getRpcUrl()).toBe(TEST_RPC);
247+
const chain = mod.getChainConfig();
248+
expect(chain.rpcUrls.default.http[0]).toBe(TEST_RPC);
249+
expect((chain.rpcUrls as any)['public'].http[0]).toBe(TEST_RPC);
250+
251+
// client reflects override
252+
const { createLitClient } = await import('@lit-protocol/lit-client');
253+
const client = await createLitClient({ network: mod });
254+
const cc = client.getChainConfig();
255+
256+
// eslint-disable-next-line no-console
257+
console.log('[rpc-override] client rpcUrl:', cc.rpcUrl);
258+
// eslint-disable-next-line no-console
259+
console.log('[rpc-override] client viem rpcUrls.default:', cc.viemConfig.rpcUrls.default.http);
260+
// eslint-disable-next-line no-console
261+
console.log('[rpc-override] client viem rpcUrls.public:', (cc.viemConfig.rpcUrls as any)['public']?.http);
262+
263+
expect(cc.rpcUrl).toBe(TEST_RPC);
264+
expect(cc.viemConfig.rpcUrls.default.http[0]).toBe(TEST_RPC);
265+
expect((cc.viemConfig.rpcUrls as any)['public'].http[0]).toBe(TEST_RPC);
266+
});
267+
});

e2e/src/init.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,21 @@ export const init = async (
109109

110110
// Dynamic import of network module
111111
const networksModule = await import('@lit-protocol/networks');
112-
const _networkModule = networksModule[config.importName];
112+
const _baseNetworkModule = networksModule[config.importName];
113+
114+
// Optional RPC override from env
115+
const rpcOverride = process.env['LIT_YELLOWSTONE_PRIVATE_RPC_URL'];
116+
const _networkModule =
117+
rpcOverride && typeof _baseNetworkModule.withOverrides === 'function'
118+
? _baseNetworkModule.withOverrides({ rpcUrl: rpcOverride })
119+
: _baseNetworkModule;
120+
121+
if (rpcOverride) {
122+
console.log(
123+
'✅ Using RPC override (LIT_YELLOWSTONE_PRIVATE_RPC_URL):',
124+
rpcOverride
125+
);
126+
}
113127

114128
// Fund accounts based on network type
115129
const isLocal = config.type === 'local';

packages/auth-services/src/_setup/initSystemContext.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ declare global {
1515
};
1616
}
1717

18-
export async function initSystemContext({ appName }: { appName: string }) {
18+
export async function initSystemContext({
19+
appName,
20+
rpcUrl,
21+
}: {
22+
appName: string;
23+
rpcUrl?: string;
24+
}) {
1925
console.log('🔥 [initSystemContext] Initializing system context...');
2026

2127
let networkModule: any;
@@ -34,8 +40,14 @@ export async function initSystemContext({ appName }: { appName: string }) {
3440
throw new Error(`Unsupported network: ${env.NETWORK}`);
3541
}
3642

43+
// Apply runtime override if rpcUrl provided
44+
const effectiveModule =
45+
rpcUrl && typeof networkModule.withOverrides === 'function'
46+
? networkModule.withOverrides({ rpcUrl })
47+
: networkModule;
48+
3749
const litClient = await createLitClient({
38-
network: networkModule,
50+
network: effectiveModule,
3951
});
4052

4153
const account = privateKeyToAccount(env.LIT_TXSENDER_PRIVATE_KEY as Hex);

packages/auth-services/src/auth-server/src/createAuthServer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,10 @@ export const createLitAuthServer = (
9191
// =============================================================
9292
// Ensure system context is initialized before server fully starts, using appName from config
9393
// This was originally at the top level, moved here to use config.appName
94-
await initSystemContext({ appName: config.appName });
94+
await initSystemContext({
95+
appName: config.appName,
96+
rpcUrl: config.litTxsenderRpcUrl,
97+
});
9598
})
9699
.get('/', () => ({
97100
message: 'PKP Auth Service is running. PKP minting is now asynchronous.',

packages/lit-client/src/lib/LitClient/createLitClient.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import {
1010
} from '@lit-protocol/access-control-conditions';
1111
import { encrypt as blsEncrypt } from '@lit-protocol/crypto';
1212
import { getChildLogger } from '@lit-protocol/logger';
13-
import type {
14-
LitNetworkModule,
15-
PKPStorageProvider,
13+
import {
14+
type LitNetworkModule,
15+
type PKPStorageProvider,
1616
} from '@lit-protocol/networks';
1717
import {
1818
AuthContextSchema2,

packages/lit-client/src/lib/LitClient/intergrations/createPkpViemAccount.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,14 @@ export async function createPKPViemAccount({
120120
tx,
121121
address,
122122
chain,
123-
transportUrl,
124123
}: {
125124
tx: Partial<TransactionSerializable>;
126125
address: `0x${string}`;
127126
chain: Chain;
128-
transportUrl: string;
129127
}): Promise<TransactionSerializable> {
130128
const client = createPublicClient({
131129
chain,
132-
transport: http(transportUrl),
130+
transport: http(chain.rpcUrls.default.http[0]),
133131
});
134132

135133
try {
@@ -246,7 +244,6 @@ export async function createPKPViemAccount({
246244
tx: txRequest,
247245
address,
248246
chain: chainConfig,
249-
transportUrl: chainConfig.rpcUrls.default.http[0],
250247
});
251248
} else {
252249
// Ensure minimum required fields for transaction type inference

packages/networks/src/networks/vNaga/shared/factories/BaseModuleFactory.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,72 @@ export function createBaseModule<T, M>(config: BaseModuleConfig<T, M>) {
10871087
},
10881088
},
10891089
},
1090+
/**
1091+
* Returns a wrapped module instance with runtime overrides while keeping the base immutable.
1092+
* Currently supports overriding the RPC URL used by consumers of this module.
1093+
*
1094+
* @param overrides - The overrides to apply to the module.
1095+
* @returns A wrapped module instance with the overrides applied.
1096+
* @example
1097+
*
1098+
* import { nagaDev } from '@lit-protocol/networks';
1099+
* const nagaDevWithOverride = nagaDev.withOverrides({ rpcUrl: 'https://custom-rpc-url.com' });
1100+
* const litClient = await createLitClient({ network: nagaDevWithOverride });
1101+
*/
1102+
withOverrides: (overrides: { rpcUrl?: string }) => {
1103+
const resolvedRpcUrl = overrides.rpcUrl ?? baseModule.getRpcUrl();
1104+
1105+
const wrapped = {
1106+
...baseModule,
1107+
1108+
getRpcUrl: () => resolvedRpcUrl,
1109+
1110+
getChainConfig: () => {
1111+
const chain = baseModule.getChainConfig();
1112+
return {
1113+
...chain,
1114+
rpcUrls: {
1115+
...chain.rpcUrls,
1116+
default: {
1117+
...chain.rpcUrls.default,
1118+
http: [resolvedRpcUrl],
1119+
},
1120+
['public']: {
1121+
...(chain.rpcUrls as any)['public'],
1122+
http: [resolvedRpcUrl],
1123+
},
1124+
},
1125+
} as typeof chain;
1126+
},
1127+
1128+
createStateManager: async <StateT, ModuleT>(params: {
1129+
callback: (params: CallbackParams) => Promise<StateT>;
1130+
networkModule: ModuleT;
1131+
}): Promise<Awaited<ReturnType<typeof createStateManager<StateT>>>> => {
1132+
const createReadOnlyChainManager = () => {
1133+
const dummyAccount = privateKeyToAccount(DEV_PRIVATE_KEY);
1134+
return createChainManager(dummyAccount);
1135+
};
1136+
1137+
const overriddenNetworkConfig = {
1138+
...networkConfig,
1139+
rpcUrl: resolvedRpcUrl,
1140+
chainConfig: wrapped.getChainConfig(),
1141+
} as typeof networkConfig;
1142+
1143+
return await createStateManager<StateT>({
1144+
networkConfig: overriddenNetworkConfig,
1145+
callback: params.callback,
1146+
networkModule: wrapped as unknown as LitNetworkModuleBase,
1147+
createReadOnlyChainManager,
1148+
});
1149+
},
1150+
} as typeof baseModule & {
1151+
withOverrides: (o: { rpcUrl?: string }) => any;
1152+
};
1153+
1154+
return wrapped;
1155+
},
10901156
};
10911157

10921158
return baseModule;

0 commit comments

Comments
 (0)