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
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"files": {
"includes": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.json", "!**/*.css"]
"includes": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.json", "worker/src/**/*.ts", "!**/*.css"]
},
"linter": {
"rules": {
Expand Down
3 changes: 3 additions & 0 deletions e2e/tests/eth-mainnet/address.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,13 @@ test.describe("Address Page", () => {
const addressPage = new AddressPage(page);
await addressPage.goto("0xinvalid");

// Invalid address may show error, loading timeout, or redirect to home
await expect(
addressPage.errorText
.or(addressPage.container)
.or(page.locator("text=Something went wrong"))
.or(page.locator("text=Data is taking longer"))
.or(page.locator("text=OPENSCAN"))
.first()
).toBeVisible({ timeout: DEFAULT_TIMEOUT * 3 });
});
Expand Down
5 changes: 5 additions & 0 deletions e2e/tests/eth-mainnet/blocks.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ test.describe("Blocks Page", () => {

await expect(blocksPage.loader).toBeHidden({ timeout: DEFAULT_TIMEOUT * 3 });

// Wait for table data to fully render (not just skeleton)
await expect(blocksPage.blockTable.locator("tbody tr td a").first()).toBeVisible({
timeout: DEFAULT_TIMEOUT * 3,
});

// Verify header has proper structure
const header = blocksPage.blocksHeader;
await expect(header).toBeVisible();
Expand Down
9 changes: 7 additions & 2 deletions e2e/tests/eth-mainnet/token.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,13 @@ test.describe("ERC1155 Token Details", () => {

const loaded = await waitForTokenContent(page, testInfo);
if (loaded) {
// Verify image container exists
await expect(page.locator(".erc1155-image-container")).toBeVisible();
// Verify image container exists or data is still loading (metadata fetch may time out)
await expect(
page
.locator(".erc1155-image-container")
.or(page.locator("text=Data is taking longer"))
.or(page.locator(".erc1155-header"))
).toBeVisible();
}
});

Expand Down
4 changes: 2 additions & 2 deletions e2e/tests/eth-mainnet/transaction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ test.describe("Transaction Page", () => {

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
// Verify input data section exists for contract interactions
await expect(page.locator("text=Input Data:")).toBeVisible();
// Verify input data exists (shown as tab in TX Analyser)
await expect(page.locator("text=Input Data").first()).toBeVisible();
}
});

Expand Down
13 changes: 6 additions & 7 deletions e2e/tests/evm-networks/arbitrum.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ test.describe("Arbitrum One - Transaction Page", () => {

// Verify gas information
await expect(page.locator("text=Gas Limit")).toBeVisible();
await expect(page.getByText("Gas Price:", { exact: true })).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Gas Price:" })).toBeVisible();
}
});

Expand Down Expand Up @@ -331,22 +331,21 @@ test.describe("Arbitrum One - Transaction Page", () => {

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
// Contract interaction should have input data
await expect(page.locator("text=Input Data:")).toBeVisible();
// Contract interaction should have input data (shown as tab in TX Analyser)
await expect(page.locator("text=Input Data").first()).toBeVisible();
}
});

test("displays other attributes section with nonce", async ({ page }, testInfo) => {
test("displays nonce and position fields", async ({ page }, testInfo) => {
const txPage = new TransactionPage(page);
const tx = ARBITRUM.transactions[UNISWAP_SWAP];

await txPage.goto(tx.hash, CHAIN_ID);

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
await expect(page.locator("text=Other Attributes:")).toBeVisible();
await expect(page.locator("text=Nonce:")).toBeVisible();
await expect(page.locator("text=Position:")).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Nonce:" })).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Position:" })).toBeVisible();
}
});

Expand Down
12 changes: 6 additions & 6 deletions e2e/tests/evm-networks/base.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,22 +250,22 @@ test.describe("Base Network - Transaction Page", () => {

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
// Contract interaction should have input data
await expect(page.locator("text=Input Data:")).toBeVisible();
// Contract interaction should have input data (shown as tab in TX Analyser)
await expect(page.locator("text=Input Data").first()).toBeVisible();
}
});

test("displays other attributes section", async ({ page }, testInfo) => {
test("displays nonce and position fields", async ({ page }, testInfo) => {
const txPage = new TransactionPage(page);
const tx = BASE.transactions[AERODROME_SWAP];

await txPage.goto(tx.hash, CHAIN_ID);

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
await expect(page.locator("text=Other Attributes:")).toBeVisible();
await expect(page.locator("text=Nonce:")).toBeVisible();
await expect(page.locator("text=Position:")).toBeVisible();
// Nonce and Position are in the transaction details grid
await expect(page.locator(".tx-label", { hasText: "Nonce:" })).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Position:" })).toBeVisible();
}
});

Expand Down
3 changes: 3 additions & 0 deletions e2e/tests/evm-networks/bsc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -782,10 +782,13 @@ test.describe("BSC Address Page - System Contracts", () => {
const addressPage = new AddressPage(page);
await addressPage.goto("0xinvalid", CHAIN_ID);

// Invalid address may show error, loading timeout, or redirect to home
await expect(
addressPage.errorText
.or(addressPage.container)
.or(page.locator("text=Something went wrong"))
.or(page.locator("text=Data is taking longer"))
.or(page.locator("text=OPENSCAN"))
.first()
).toBeVisible({ timeout: DEFAULT_TIMEOUT * 3 });
});
Expand Down
26 changes: 14 additions & 12 deletions e2e/tests/evm-networks/optimism.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ test.describe("Optimism - Transaction Page", () => {

// Verify gas information
await expect(page.locator("text=Gas Limit")).toBeVisible();
await expect(page.getByText("Gas Price:", { exact: true })).toBeVisible();
await expect(page.locator("text=Gas Price").first()).toBeVisible();
}
});

Expand Down Expand Up @@ -351,12 +351,13 @@ test.describe("Optimism - Transaction Page", () => {

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
await expect(page.locator("text=Other Attributes:")).toBeVisible();
await expect(page.locator("text=Nonce:")).toBeVisible();
await expect(page.locator("text=Position:")).toBeVisible();
// Nonce and Position are in the transaction details grid
await expect(page.locator(".tx-label", { hasText: "Nonce:" })).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Position:" })).toBeVisible();

// Verify nonce value is displayed (use locator that includes the label)
await expect(page.locator(`text=Nonce: ${tx.nonce}`)).toBeVisible();
// Verify nonce value
const nonceRow = page.locator(".tx-row", { hasText: "Nonce:" });
await expect(nonceRow.locator(".tx-value")).toContainText(String(tx.nonce));
}
});

Expand Down Expand Up @@ -406,11 +407,12 @@ test.describe("Optimism - Transaction Page", () => {

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
await expect(page.locator("text=Nonce:")).toBeVisible();
await expect(page.locator("text=Position:")).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Nonce:" })).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Position:" })).toBeVisible();

// Verify nonce value is displayed (use locator that includes the label)
await expect(page.locator(`text=Nonce: ${tx.nonce}`)).toBeVisible();
// Verify nonce value
const nonceRow = page.locator(".tx-row", { hasText: "Nonce:" });
await expect(nonceRow.locator(".tx-value")).toContainText(String(tx.nonce));
}
});

Expand Down Expand Up @@ -458,8 +460,8 @@ test.describe("Optimism - Transaction Page", () => {

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
// Contract interaction should have input data
await expect(page.locator("text=Input Data:")).toBeVisible();
// Contract interaction should have input data (shown as tab in TX Analyser)
await expect(page.locator("text=Input Data").first()).toBeVisible();
}
});

Expand Down
37 changes: 20 additions & 17 deletions e2e/tests/evm-networks/polygon.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,11 @@ test.describe("Polygon Transaction Page", () => {
await expect(page.locator("text=To:")).toBeVisible();

// Verify gas information
await expect(page.getByText("Gas Price:", { exact: true })).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Gas Price:" })).toBeVisible();
await expect(page.locator("text=Gas Limit")).toBeVisible();

// Verify has input data (NFT transfer)
await expect(page.locator("text=Input Data:")).toBeVisible();
// Verify has input data (shown as tab in TX Analyser)
await expect(page.locator("text=Input Data").first()).toBeVisible();
}
});

Expand Down Expand Up @@ -412,7 +412,7 @@ test.describe("Polygon Transaction Page", () => {
await expect(page.locator("text=Status:")).toBeVisible();
await expect(page.locator("text=Block:")).toBeVisible();
await expect(page.locator("text=Gas Limit")).toBeVisible();
await expect(page.locator("text=Input Data:")).toBeVisible();
await expect(page.locator("text=Input Data").first()).toBeVisible();
}
});

Expand Down Expand Up @@ -443,7 +443,7 @@ test.describe("Polygon Transaction Page", () => {
await expect(page.locator("text=Transaction Hash:")).toBeVisible();
await expect(page.locator("text=Status:")).toBeVisible();
await expect(page.locator("text=Gas Limit")).toBeVisible();
await expect(page.locator("text=Input Data:")).toBeVisible();
await expect(page.locator("text=Input Data").first()).toBeVisible();
}
});

Expand All @@ -455,12 +455,12 @@ test.describe("Polygon Transaction Page", () => {

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
await expect(page.locator("text=Other Attributes:")).toBeVisible();
await expect(page.locator("text=Nonce:")).toBeVisible();
await expect(page.locator("text=Position:")).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Nonce:" })).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Position:" })).toBeVisible();

// Verify position value (nonce is very large, just check it's displayed)
await expect(page.locator(`text=Position: ${tx.position}`)).toBeVisible();
// Verify position value
const posRow = page.locator(".tx-row", { hasText: "Position:" });
await expect(posRow.locator(".tx-value")).toContainText(String(tx.position));
}
});

Expand All @@ -472,10 +472,11 @@ test.describe("Polygon Transaction Page", () => {

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
await expect(page.locator("text=Nonce:")).toBeVisible();
await expect(page.locator("text=Position:")).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Nonce:" })).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Position:" })).toBeVisible();

await expect(page.locator(`text=Nonce: ${tx.nonce}`)).toBeVisible();
const nonceRow = page.locator(".tx-row", { hasText: "Nonce:" });
await expect(nonceRow.locator(".tx-value")).toContainText(String(tx.nonce));
}
});

Expand All @@ -487,11 +488,13 @@ test.describe("Polygon Transaction Page", () => {

const loaded = await waitForTxContent(page, testInfo);
if (loaded) {
await expect(page.locator("text=Nonce:")).toBeVisible();
await expect(page.locator("text=Position:")).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Nonce:" })).toBeVisible();
await expect(page.locator(".tx-label", { hasText: "Position:" })).toBeVisible();

await expect(page.locator(`text=Nonce: ${tx.nonce}`)).toBeVisible();
await expect(page.locator(`text=Position: ${tx.position}`)).toBeVisible();
const nonceRow = page.locator(".tx-row", { hasText: "Nonce:" });
await expect(nonceRow.locator(".tx-value")).toContainText(String(tx.nonce));
const posRow = page.locator(".tx-row", { hasText: "Position:" });
await expect(posRow.locator(".tx-value")).toContainText(String(tx.position));
}
});

Expand Down
22 changes: 22 additions & 0 deletions src/components/pages/settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ const getAlchemyBtcUrl = (networkId: string, apiKey: string): string | null => {
const isInfuraUrl = (url: string): boolean => url.includes("infura.io");
const isAlchemyUrl = (url: string): boolean => url.includes("alchemy.com");

const isWorkerAlchemyUrl = (url: string): boolean =>
url.includes("/evm/alchemy/") || url.includes("/btc/alchemy") || url.includes("/beacon/alchemy/");
const isWorkerInfuraUrl = (url: string): boolean => url.includes("/evm/infura/");
const isWorkerDrpcUrl = (url: string): boolean =>
url.includes("/evm/drpc/") || url.includes("/btc/drpc");
const isWorkerAnkrUrl = (url: string): boolean =>
url.includes("/evm/ankr/") || url.includes("/btc/ankr");
const isWorkerOnfinalityUrl = (url: string): boolean => url.includes("/btc/onfinality/");

const Settings: React.FC = () => {
const { t, i18n } = useTranslation("settings");
const { t: tTooltips } = useTranslation("tooltips");
Expand Down Expand Up @@ -606,6 +615,14 @@ const Settings: React.FC = () => {

const getRpcTagClass = useCallback(
(url: string): string => {
if (
isWorkerAlchemyUrl(url) ||
isWorkerInfuraUrl(url) ||
isWorkerDrpcUrl(url) ||
isWorkerAnkrUrl(url) ||
isWorkerOnfinalityUrl(url)
)
return "rpc-opensource";
if (isInfuraUrl(url) || isAlchemyUrl(url)) return "rpc-tracking";
const ep = metadataUrlMap.get(url);
if (!ep) return "";
Expand All @@ -618,6 +635,11 @@ const Settings: React.FC = () => {

const getRpcTagLabel = useCallback(
(url: string): string => {
if (isWorkerAlchemyUrl(url)) return "OpenScan Alchemy";
if (isWorkerInfuraUrl(url)) return "OpenScan Infura";
if (isWorkerDrpcUrl(url)) return "OpenScan dRPC";
if (isWorkerAnkrUrl(url)) return "OpenScan Ankr";
if (isWorkerOnfinalityUrl(url)) return "OpenScan OnFinality";
if (isInfuraUrl(url)) return "Infura Personal";
if (isAlchemyUrl(url)) return "Alchemy Personal";
const ep = metadataUrlMap.get(url);
Expand Down
5 changes: 5 additions & 0 deletions src/config/workerConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** Base URL for the OpenScan Cloudflare Worker proxy */
export const OPENSCAN_WORKER_URL =
// biome-ignore lint/complexity/useLiteralKeys: env var access
process.env["REACT_APP_OPENSCAN_WORKER_URL"] ??
"https://openscan-worker-proxy.openscan.workers.dev";
5 changes: 1 addition & 4 deletions src/hooks/useEtherscan.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { useEffect, useState } from "react";
import { OPENSCAN_WORKER_URL } from "../config/workerConfig";
import { useSettings } from "../context/SettingsContext";
import { logger } from "../utils/logger";
import type { SourcifyContractDetails } from "./useSourcify";

const ETHERSCAN_V2_API = "https://api.etherscan.io/v2/api";
const OPENSCAN_WORKER_URL =
// biome-ignore lint/complexity/useLiteralKeys: env var access
process.env["REACT_APP_OPENSCAN_WORKER_URL"] ??
"https://openscan-groq-ai-proxy.openscan.workers.dev";

interface EtherscanSourceResult {
SourceCode: string;
Expand Down
6 changes: 1 addition & 5 deletions src/utils/contractLookup.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { OPENSCAN_WORKER_URL } from "../config/workerConfig";
import { logger } from "./logger";

export interface ContractInfo {
Expand All @@ -9,11 +10,6 @@ export interface ContractInfo {
// Session-level cache keyed by "chainId:address"
const cache = new Map<string, ContractInfo | null>();

const OPENSCAN_WORKER_URL =
// biome-ignore lint/complexity/useLiteralKeys: env var access
process.env["REACT_APP_OPENSCAN_WORKER_URL"] ??
"https://openscan-groq-ai-proxy.openscan.workers.dev";

/**
* Fetch contract verification from Etherscan V2 API.
* Uses user-provided key directly, or proxies through the OpenScan Worker.
Expand Down
Loading
Loading