diff --git a/src/bundler/utils/Utils.ts b/src/bundler/utils/Utils.ts index 700a2bb59..7587b3749 100644 --- a/src/bundler/utils/Utils.ts +++ b/src/bundler/utils/Utils.ts @@ -1,23 +1,50 @@ -export const extractChainIdFromBundlerUrl = (url: string): number => { +/** + * Extracts the chain ID from a given URL. + * + * @param url - The URL to extract the chain ID from. + * @returns The extracted chain ID as a number. + * @throws {Error} If the chain ID is not found in the URL or is invalid. + * + * @example + * // Returns 80001 + * extractChainIdFromUrl("https://example.com/api/v2/80001/rpc") + */ +export const extractChainIdFromUrl = (url: string): number => { try { - const regex = /\/api\/v2\/(\d+)\/[a-zA-Z0-9.-]+$/ - // biome-ignore lint/style/noNonNullAssertion: - const match = regex.exec(url)! - return Number.parseInt(match[1]) - } catch (error) { - throw new Error("Invalid chain id") - } -} - -export const extractChainIdFromPaymasterUrl = (url: string): number => { - try { - const regex = /\/api\/v\d+\/(\d+)\// - const match = regex.exec(url) + const regex = /\/(\d+)\//; + const match = regex.exec(new URL(url).pathname); if (!match) { - throw new Error("Invalid URL format") + throw new Error("Chain ID not found in URL"); } - return Number.parseInt(match[1]) + const chainId = Number.parseInt(match[1], 10); + if (Number.isNaN(chainId)) { + throw new Error("Invalid chain ID"); + } + return chainId; } catch (error) { - throw new Error("Invalid chain id") + if (error instanceof Error) { + throw new Error(`Invalid chain id: ${error.message}`); + } + throw new Error("Invalid chain id"); } -} +}; + +/** + * Extracts the chain ID from a bundler URL. + * + * @param url - The bundler URL to extract the chain ID from. + * @returns The extracted chain ID as a number. + * @throws {Error} If the chain ID is not found in the URL or is invalid. + */ +export const extractChainIdFromBundlerUrl = (url: string): number => + extractChainIdFromUrl(url); + +/** + * Extracts the chain ID from a paymaster URL. + * + * @param url - The paymaster URL to extract the chain ID from. + * @returns The extracted chain ID as a number. + * @throws {Error} If the chain ID is not found in the URL or is invalid. + */ +export const extractChainIdFromPaymasterUrl = (url: string): number => + extractChainIdFromUrl(url); \ No newline at end of file diff --git a/tests/bundler/read.test.ts b/tests/bundler/read.test.ts index 2b8c27b4b..2dcbca7a5 100644 --- a/tests/bundler/read.test.ts +++ b/tests/bundler/read.test.ts @@ -6,9 +6,8 @@ import { type BiconomySmartAccountV2Config, compareChainIds, createSmartAccountClient, - getCustomChain } from "../../src/account" -import { createBundler } from "../../src/bundler" +import { createBundler, extractChainIdFromUrl } from "../../src/bundler" import { getBundlerUrl, getConfig } from "../utils" describe("Bundler:Read", () => { @@ -51,11 +50,11 @@ describe("Bundler:Read", () => { }) ) ) - ;[smartAccountAddress, smartAccountAddressTwo] = await Promise.all( - [smartAccount, smartAccountTwo].map((account) => - account.getAccountAddress() + ;[smartAccountAddress, smartAccountAddressTwo] = await Promise.all( + [smartAccount, smartAccountTwo].map((account) => + account.getAccountAddress() + ) ) - ) }) test.concurrent( @@ -200,4 +199,30 @@ describe("Bundler:Read", () => { await expect(createAccount).rejects.toThrow() } ) + + test.concurrent('extracts chain ID from various URL structures', () => { + const testCases = [ + { url: "https://example.com/api/v2/1234/endpoint", expected: 1234 }, + { url: "https://api.example.com/v1/5678/resource", expected: 5678 }, + { url: "http://localhost:3000/api/9876/action", expected: 9876 }, + { url: "https://example.com/1234/api/v2/endpoint", expected: 1234 }, + { url: "https://example.com/network/5678/resource/action", expected: 5678 }, + { url: "https://api.example.com/prefix/9876/suffix", expected: 9876 }, + { url: "https://example.com/api/v2/1234/5678/endpoint", expected: 1234 }, + { url: "https://example.com/api/v2/endpoint/1234/", expected: 1234 }, + { url: "https://example.com/api/v2/1234/endpoint?param=value", expected: 1234 }, + { url: "https://example.com/api/v2/1234/endpoint#section", expected: 1234 }, + { url: "https://subdomain.example.com/api/1234/endpoint", expected: 1234 }, + { url: "http://192.168.1.1/api/1234/endpoint", expected: 1234 }, + { url: "https://user:pass@example.com/api/1234/endpoint", expected: 1234 }, + { url: "https://example.com/1234/", expected: 1234 }, + { url: "https://api.example.com/v1/chain/5678/details", expected: 5678 }, + { url: "https://paymaster.biconomy.io/api/v1/80001/-RObQRX9ei.fc6918eb-c582-4417-9d5a-0507b17cfe71", expected: 80001 }, + { url: "https://bundler.biconomy.io/api/v2/80002/nJPK7B3ru.dd7f7861-190d-41bd-af80-6877f74b8f44", expected: 80002 }, + ]; + + for (const { url, expected } of testCases) { + expect(extractChainIdFromUrl(url)).toBe(expected); + } + }) })