Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .github/workflows/e2e-evm-networks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ jobs:
e2e-evm:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
shard:
- name: shard-a
tests: "e2e/tests/evm-networks/arbitrum.spec.ts e2e/tests/evm-networks/base.spec.ts e2e/tests/evm-networks/optimism.spec.ts"
- name: shard-b
tests: "e2e/tests/evm-networks/bsc.spec.ts e2e/tests/evm-networks/polygon.spec.ts"

steps:
- uses: actions/checkout@v4
Expand All @@ -27,8 +35,8 @@ jobs:
- name: Build application
run: ./scripts/build-production.sh

- name: Run EVM Networks E2E tests
run: bun run test:e2e:evm-networks
- name: Run EVM Networks E2E tests (${{ matrix.shard.name }})
run: bunx playwright test ${{ matrix.shard.tests }}
env:
CI: true
INFURA_API_KEY: ${{ secrets.INFURA_API_KEY }}
Expand All @@ -38,6 +46,6 @@ jobs:
uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report-evm
name: playwright-report-evm-${{ matrix.shard.name }}
path: playwright-report/
retention-days: 7
56 changes: 42 additions & 14 deletions src/components/pages/rpcs/RpcTestRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,42 @@ function getProviderLabel(url: string, metadata: MetadataRpcEndpoint | undefined
}
}

function redactSensitiveUrl(rawUrl: string): string {
try {
const parsed = new URL(rawUrl);

// Hide common credential query params
const sensitiveParamRegex = /key|token|secret|auth|signature|apikey|api_key|access_token/i;
for (const [key] of parsed.searchParams.entries()) {
if (sensitiveParamRegex.test(key)) {
parsed.searchParams.set(key, "***");
}
}

// Hide credential-like path segments (long, high-entropy tokens)
const segments = parsed.pathname.split("/").map((segment) => {
if (!segment) return segment;
const looksLikeToken = segment.length >= 24 && /[A-Za-z]/.test(segment) && /\d/.test(segment);
return looksLikeToken ? "***" : segment;
});
parsed.pathname = segments.join("/");

return parsed.toString();
} catch {
return rawUrl;
}
}

function getTruncatedUrl(url: string): string {
const safeUrl = redactSensitiveUrl(url);
try {
const parsed = new URL(url);
const parsed = new URL(safeUrl);
const path = parsed.pathname === "/" ? "" : parsed.pathname;
const display = `${parsed.hostname}${path}`;
return display.length > 50 ? `${display.slice(0, 47)}...` : display;
const query = parsed.search ? parsed.search : "";
const display = `${parsed.hostname}${path}${query}`;
return display.length > 70 ? `${display.slice(0, 67)}...` : display;
} catch {
return url.length > 50 ? `${url.slice(0, 47)}...` : url;
return safeUrl.length > 70 ? `${safeUrl.slice(0, 67)}...` : safeUrl;
}
}

Expand All @@ -62,9 +90,11 @@ const RpcTestRow: React.FC<RpcTestRowProps> = ({ url, metadata, result, isActive
const provider = getProviderLabel(url, metadata);
const trackingClass = getTrackingClass(metadata);

const safeUrl = redactSensitiveUrl(url);

const handleCopy = useCallback(() => {
navigator.clipboard.writeText(url);
}, [url]);
navigator.clipboard.writeText(safeUrl);
}, [safeUrl]);

const getTrackingLabel = (): string => {
if (!metadata) return "";
Expand All @@ -78,7 +108,7 @@ const RpcTestRow: React.FC<RpcTestRowProps> = ({ url, metadata, result, isActive
{/* Provider */}
<div className="rpcs-cell rpcs-cell-provider">
<span className="rpcs-provider-name">{provider}</span>
<span className="rpcs-provider-url" title={url}>
<span className="rpcs-provider-url" title={safeUrl}>
{getTruncatedUrl(url)}
</span>
</div>
Expand All @@ -87,8 +117,8 @@ const RpcTestRow: React.FC<RpcTestRowProps> = ({ url, metadata, result, isActive
<div className="rpcs-cell rpcs-cell-latency">
{status === "pending" ? (
<span className="rpcs-latency-pending">...</span>
) : status === "offline" ? (
<span className="rpcs-latency-value rpcs-latency-slow">{t("latency.ms", { ms: 0 })}</span>
) : status === "offline" || status === "timeout" || status === "untested" ? (
<span className="rpcs-latency-na">{t("latency.na")}</span>
) : result?.latency != null ? (
<span
className={`rpcs-latency-value ${result.latency < 300 ? "rpcs-latency-fast" : result.latency < 1000 ? "rpcs-latency-medium" : "rpcs-latency-slow"}`}
Expand Down Expand Up @@ -157,16 +187,14 @@ const RpcTestRow: React.FC<RpcTestRowProps> = ({ url, metadata, result, isActive
<span className={getStatusDotClass(status)} />
<span className="rpcs-provider-name">{provider}</span>
</div>
<span className="rpcs-provider-url" title={url}>
<span className="rpcs-provider-url" title={safeUrl}>
{getTruncatedUrl(url)}
</span>
<div className="rpcs-mobile-stats">
{status === "pending" ? (
<span className="rpcs-latency-pending">...</span>
) : status === "offline" ? (
<span className="rpcs-latency-value rpcs-latency-slow">
{t("latency.ms", { ms: 0 })}
</span>
) : status === "offline" || status === "timeout" || status === "untested" ? (
<span className="rpcs-latency-na">{t("latency.na")}</span>
) : result?.latency != null ? (
<span
className={`rpcs-latency-value ${result.latency < 300 ? "rpcs-latency-fast" : result.latency < 1000 ? "rpcs-latency-medium" : "rpcs-latency-slow"}`}
Expand Down
5 changes: 2 additions & 3 deletions src/components/pages/rpcs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,13 @@ const RPCs: React.FC = () => {
[setSearchParams, clearResults],
);

// Auto-test when network selection changes (endpoints and testAll derive from it)
// biome-ignore lint/correctness/useExhaustiveDependencies: only re-run on network change
// Auto-test when selected network/endpoints change
useEffect(() => {
if (endpoints.length > 0) {
const urls = endpoints.map((e) => e.url);
testAll(urls, networkType);
}
}, [selectedNetworkId]);
}, [endpoints, testAll, networkType]);

// Handle test all
const handleTestAll = useCallback(() => {
Expand Down
19 changes: 19 additions & 0 deletions src/components/pages/rpcs/useRpcLatencyTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ export interface RpcTestResult {
const TIMEOUT_MS = 10_000;
const BATCH_SIZE = 6;

function isHttpRpcUrl(url: string): boolean {
try {
const protocol = new URL(url).protocol.toLowerCase();
return protocol === "http:" || protocol === "https:";
} catch {
return false;
}
}

function buildRpcBody(networkType: NetworkType): string {
if (networkType === "bitcoin") {
return JSON.stringify({ jsonrpc: "2.0", id: 1, method: "getblockcount", params: [] });
Expand All @@ -27,6 +36,16 @@ export async function testRpcEndpoint(
signal: AbortSignal,
networkType: NetworkType,
): Promise<RpcTestResult> {
if (!isHttpRpcUrl(url)) {
return {
url,
status: "untested",
latency: null,
blockNumber: null,
error: "Unsupported RPC protocol for HTTP test",
};
}

const start = performance.now();
try {
const res = await fetch(url, {
Expand Down
2 changes: 1 addition & 1 deletion src/locales/en/rpcs.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"pageTitle": "RPC Endpoints",
"description": "Test and compare RPC endpoint performance across networks. Add fast endpoints to your configuration with one click.",
"description": "Test and compare RPC endpoint performance across networks. Re-test providers and copy endpoint URLs for your configuration.",
"networkSelector": {
"label": "Select Network",
"placeholder": "Choose a network..."
Expand Down
2 changes: 1 addition & 1 deletion src/locales/es/rpcs.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"pageTitle": "Endpoints RPC",
"description": "Probá y compará el rendimiento de los endpoints RPC en distintas redes. Agregá endpoints rápidos a tu configuración con un click.",
"description": "Compará el rendimiento de los endpoints RPC por red, volvé a probar proveedores y copiá las URLs para tu configuración.",
"networkSelector": {
"label": "Seleccionar Red",
"placeholder": "Elegí una red..."
Expand Down