Skip to content

test: cross chain e2e tests - #420

Open
0xLaruku wants to merge 5 commits into
devfrom
test/cross-chain-e2e-tests
Open

test: cross chain e2e tests#420
0xLaruku wants to merge 5 commits into
devfrom
test/cross-chain-e2e-tests

Conversation

@0xLaruku

Copy link
Copy Markdown
Contributor

Summary

  • Removed the Anvil fork entirely: Tests now hit real testnet RPCs.
  • Added a real cross-chain flow test covering the critical path end to end.
  • Dropped mocks from the cross-chain specs.

Test suite reorganization

  • Consolidated demo-limits.spec.ts and mint.spec.ts into cross-chain.spec.ts as the Demo limits and Mint mockUSDC describe blocks; deleted the standalone files.
  • Grouped the remaining specs into clear describe blocks (Asset Discovery, Recipient address, Amount validation, Cross-chain intents, Build quote, Address menu, Negative test, Demo limits, Mint mockUSDC).
  • addresses-build.spec.ts: select the Ethereum chain option via .last() instead of .first() to target the intended dropdown entry.

Walletless config

  • e2eConnector.ts: the E2E signing account is now sourced from NEXT_PUBLIC_E2E_PRIVATE_KEY, so the headless connector signs as a real, funded testnet wallet.

CI

  • test-e2e.yml: removed the Foundry install step and the NEXT_PUBLIC_ANVIL_URL / NEXT_PUBLIC_ANVIL_CHAIN_ID env;
  • build-lint.yml: pass NEXT_PUBLIC_E2E_PRIVATE_KEY to the build step.
  • playwright.config.ts / .env.e2e: removed the Anvil fork webServer and the Anvil env vars.

Required setup (before merge)

These repo secrets must exist for CI to pass:

  • NEXT_PUBLIC_E2E_PRIVATE_KEY — a dedicated, low-balance, rotatable testnet account, funded with testnet ETH on every chain the flow touches (gas for mint + intent execution).

@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
interop-sdk-addresses Ready Ready Preview, Comment Jun 18, 2026 7:28pm
interop-sdk-benchmark Ready Ready Preview, Comment Jun 18, 2026 7:28pm
interop-sdk-examples Ready Ready Preview, Comment Jun 18, 2026 7:28pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
interop-sdk-docs Skipped Skipped Jun 18, 2026 7:28pm

Request Review

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Cross-chain Playwright E2E tests against real testnets (no Anvil fork)
🧪 Tests ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

Description

• Switch cross-chain E2E suite from Anvil fork + mocks to real testnet RPC execution.
• Add an end-to-end cross-chain intent execution test covering the critical user path.
• Update CI and Playwright config to inject an E2E signing key and remove Foundry/Anvil wiring.
Diagram

sequenceDiagram
  participant PW as Playwright
  participant UI as Next.js UI
  participant W as Walletless E2E Provider
  participant SRC as Sepolia RPC
  participant Q as Quote Provider
  participant DST as Base Sepolia RPC

  PW->>UI: Navigate /cross-chain?testnet=true
  UI->>W: Initialize provider (NEXT_PUBLIC_E2E_PRIVATE_KEY)
  PW->>UI: Select chains/tokens + amount
  UI->>Q: Get quotes
  PW->>UI: Execute selected route
  UI->>SRC: Sign + send tx (source chain)
  Q->>DST: Relay/settle on destination
  UI-->>PW: Show "Order Filled Successfully!"
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Hybrid E2E: keep execution real, mock quote aggregation
  • ➕ More deterministic and faster while still validating on-chain signing/sending.
  • ➕ Reduces flakiness from third-party quote providers and rate limits.
  • ➖ Less confidence in the full quote-to-execute critical path (integration gaps can slip through).
  • ➖ Requires maintaining stable mocked quote fixtures/calldata.
2. Deterministic chain snapshot (Anvil/Tenderly fork) as PR-gate; live testnets nightly
  • ➕ PR-gate remains reliable and repeatable; live-testnet failures don’t block merges.
  • ➕ Still catches real-network integration issues on a schedule.
  • ➖ Two pipelines to maintain (fork + live).
  • ➖ Nightly failures may be noticed later than PR-time.
3. Dedicated ephemeral test environment (self-hosted RPC + funded keys)
  • ➕ Strongest isolation and repeatability while preserving realism.
  • ➕ Avoids public RPC throttling and shared-state issues.
  • ➖ Higher infra/ops cost; more moving parts to provision and secure.
  • ➖ Longer setup time to implement.

Recommendation: If the goal is to validate the true production-like critical path, the PR’s approach is appropriate—but consider making the live-testnet “executes a cross-chain intent” test a smoke test (or nightly) if it proves flaky. For PR-gating reliability, a hybrid approach (mock quotes + real signing/sending) is the best compromise if stability becomes an issue.

Files changed (7) +111 / -176

Enhancement (1) +1 / -0
e2eConnector.tsConfigure walletless E2E provider from NEXT_PUBLIC_E2E_PRIVATE_KEY +1/-0

Configure walletless E2E provider from NEXT_PUBLIC_E2E_PRIVATE_KEY

• Updates the walletless E2E provider to source its signing account from NEXT_PUBLIC_E2E_PRIVATE_KEY, enabling headless E2E runs with a real funded testnet wallet.

examples/ui/app/cross-chain/config/e2eConnector.ts

Tests (2) +108 / -159
addresses-build.spec.tsStabilize Ethereum selection in chain dropdown +1/-1

Stabilize Ethereum selection in chain dropdown

• Selects the intended 'Ethereum' dropdown entry via .last() instead of .first(), reducing ambiguity when multiple matches exist.

examples/ui/tests/addresses-build.spec.ts

cross-chain.spec.tsRewrite cross-chain spec to remove mocks and add real execution flow +107/-158

Rewrite cross-chain spec to remove mocks and add real execution flow

• Removes token/quote request mocking and updates expectations to match the real E2E signing account. Adds a full cross-chain intent execution test (Sepolia → Base Sepolia) and consolidates demo limits + mint mockUSDC tests into this file.

examples/ui/tests/cross-chain.spec.ts

Other (4) +2 / -17
build-lint.ymlPass E2E signing key into build job +1/-0

Pass E2E signing key into build job

• Injects NEXT_PUBLIC_E2E_PRIVATE_KEY into the CI build environment so UI code that references the variable can compile consistently in CI.

.github/workflows/build-lint.yml

test-e2e.ymlRemove Foundry/Anvil setup and run E2E against real RPCs +1/-5

Remove Foundry/Anvil setup and run E2E against real RPCs

• Drops the Foundry install step and removes Anvil-specific env vars. Adds NEXT_PUBLIC_E2E_PRIVATE_KEY to the Playwright test environment so tests can sign and submit real testnet transactions.

.github/workflows/test-e2e.yml

.env.e2eRemove Anvil env vars from local E2E env file +0/-2

Remove Anvil env vars from local E2E env file

• Keeps only NEXT_PUBLIC_E2E=true, eliminating NEXT_PUBLIC_ANVIL_URL and NEXT_PUBLIC_ANVIL_CHAIN_ID from the E2E dotenv file.

examples/ui/.env.e2e

playwright.config.tsStop booting Anvil fork webServer for Playwright +0/-10

Stop booting Anvil fork webServer for Playwright

• Removes the anvil URL/port calculation and the webServer entry that ran start-anvil-fork.mjs, leaving only the Next.js build+start server for tests.

examples/ui/playwright.config.ts

@aws-amplify-us-east-2

Copy link
Copy Markdown

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-420.d1mklc3hz0pq3a.amplifyapp.com

@qodo-code-review

qodo-code-review Bot commented Jun 18, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (6) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Unmocked OIF stalls E2E 🐞 Bug ☼ Reliability
Description
cross-chain.spec.ts no longer aborts OIF network traffic, but the app still instantiates the OIF
provider and eagerly prefetches discoverAssets(), making page readiness dependent on
https://oif-api.openzeppelin.com/api latency/availability and causing Playwright timeouts/flakes.
This can impact multiple tests because the warm-cache prefetch happens as soon as an executor is
created, not only when OIF is explicitly selected.
Code

examples/ui/tests/cross-chain.spec.ts[R3-5]

+test.beforeEach(async ({ page }) => {
  await page.goto('/cross-chain?testnet=true');
});
Evidence
The tests now only navigate to the page, while the app code still constructs an OIF provider
pointing at the OIF API URL and prefetches discovery on executor creation, which can introduce
external-call latency into test startup.

examples/ui/tests/cross-chain.spec.ts[1-5]
examples/ui/app/cross-chain/services/sdk.ts[17-18]
examples/ui/app/cross-chain/services/sdk.ts[58-66]
examples/ui/app/cross-chain/services/sdk.ts[88-97]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The E2E suite removed the request interception that previously aborted OIF API calls, but the app still constructs an OIF provider and prefetches discovery. This makes E2E runs sensitive to OIF endpoint slowness/outages, leading to long hangs and flaky timeouts.

## Issue Context
Even if tests primarily exercise Across/Relay/LI.FI, `getExecutor()` prefetches discovery for both executor instances, which can trigger OIF network traffic.

## Fix Focus Areas
- examples/ui/tests/cross-chain.spec.ts[1-6]
- examples/ui/app/cross-chain/services/sdk.ts[17-97]

## Suggested fix
Reintroduce a fast-fail route abort for OIF in Playwright (e.g., `context.route('**/oif-api.openzeppelin.com/**', route => route.abort('blockedbyclient'))`), or add an E2E-only switch in `buildExecutor()` to skip adding the OIF provider during E2E runs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Client-bundled private key 🐞 Bug ⛨ Security
Description
The cross-chain E2E connector reads process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY in client-side code,
so any build produced with that env set will ship the private key to the browser runtime where it
can be extracted. CI workflows also inject this secret into build/test steps, increasing the chance
of accidental exposure via build artifacts/logging/misdeployment.
Code

examples/ui/app/cross-chain/config/e2eConnector.ts[10]

+  account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',
Evidence
The app constructs an E2E signing provider from process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY
(client-side), and CI workflows explicitly inject that secret into build/test environments, meaning
the key is expected to be available in runtime where browser code can access it.

examples/ui/app/cross-chain/config/e2eConnector.ts[7-17]
.github/workflows/build-lint.yml[34-39]
.github/workflows/test-e2e.yml[43-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`NEXT_PUBLIC_E2E_PRIVATE_KEY` is consumed in client-side code to create the walletless E2E provider. If that env var is present during a build that is deployed/shared, the resulting client bundle/runtime contains the private key and it can be extracted.

## Issue Context
This PR also passes `NEXT_PUBLIC_E2E_PRIVATE_KEY` into CI build/test workflows, making it more likely the key ends up embedded in built assets.

## Fix
Refactor so the private key is not part of the Next.js client bundle:
- Do **not** use a `NEXT_PUBLIC_*` env var for a private key.
- Prefer passing the key only to the Playwright runner and injecting it at runtime (e.g., `page.addInitScript(...)` to set a global like `globalThis.__E2E_PRIVATE_KEY`, then have the E2E connector read that global only when `NEXT_PUBLIC_E2E === 'true'`).
- Remove passing the secret to non-E2E build steps unless strictly required.

## Fix Focus Areas
- examples/ui/app/cross-chain/config/e2eConnector.ts[1-26]
- .github/workflows/build-lint.yml[34-39]
- .github/workflows/test-e2e.yml[43-49]
- examples/ui/playwright.config.ts[1-36]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Stale Anvil E2E docs 🐞 Bug ⚙ Maintainability
Description
Playwright no longer starts an Anvil fork and .env.e2e no longer contains NEXT_PUBLIC_ANVIL_*,
but the README, .env.example, and publicClient.ts still describe/consume Anvil settings, leaving
contributors with incorrect E2E setup instructions. This configuration drift increases the chance of
broken local runs and confusing future maintenance (e.g., someone sets NEXT_PUBLIC_ANVIL_URL
expecting it to be used).
Code

examples/ui/playwright.config.ts[R28-31]

  webServer: [
-    {
-      command: 'node ./scripts/start-anvil-fork.mjs',
-      port: anvilPort,
-      env: { ANVIL_PORT: String(anvilPort) },
-      reuseExistingServer: !process.env.CI,
-      timeout: 60_000,
-    },
    {
      command: 'pnpm build && pnpm start',
      url: 'http://localhost:3000',
Evidence
Playwright is configured to only build/start the app (no Anvil), .env.e2e contains only
NEXT_PUBLIC_E2E=true, while docs and runtime config still reference Anvil env vars and Anvil-based
boot flow.

examples/ui/playwright.config.ts[28-35]
examples/ui/.env.e2e[1-1]
examples/ui/README.md[30-44]
examples/ui/.env.example[8-16]
examples/ui/app/cross-chain/config/publicClient.ts[5-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR removes the Anvil webServer setup and Anvil env vars from `.env.e2e`, but documentation and runtime config still reference Anvil. This creates misleading setup guidance and leftover configuration paths.

## Issue Context
README and `.env.example` still instruct users that Playwright boots an anvil fork and list Anvil-related env vars; `publicClient.ts` still reads `NEXT_PUBLIC_ANVIL_URL` / `NEXT_PUBLIC_ANVIL_CHAIN_ID`.

## Fix Focus Areas
- examples/ui/playwright.config.ts[1-35]
- examples/ui/.env.e2e[1-1]
- examples/ui/README.md[19-44]
- examples/ui/.env.example[8-16]
- examples/ui/app/cross-chain/config/publicClient.ts[5-10]

## Suggested fix
Update README and `.env.example` to reflect the new real-RPC E2E flow and document `NEXT_PUBLIC_E2E_PRIVATE_KEY` usage; either remove the `NEXT_PUBLIC_ANVIL_*` branch from `publicClient.ts` or clearly mark it deprecated/unused and ensure it cannot be accidentally relied upon.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Brittle .last() selector 🐞 Bug ☼ Reliability
Description
The cross-chain execution test clicks getByRole('button', { name: 'Get Quotes' }).last(), but the
UI also renders a separate 'Get Quotes' button as the mode tab label, making the locator dependent
on DOM ordering and potentially clicking the tab instead of the submit button. This can cause the
test to not fetch quotes and fail downstream when provider buttons/Execute never appear.
Code

examples/ui/tests/cross-chain.spec.ts[R117-123]

+    await page.getByRole('textbox', { name: 'Amount' }).fill('0.03');
+    await page.getByRole('button', { name: 'Get Quotes' }).last().click();
+    await page
+      .locator('button')
+      .filter({ hasText: /Relay/ })
+      .click();
+    await page.getByRole('button', { name: 'Execute' }).click();
Evidence
The test uses .last() on a duplicated accessible name, and the UI code shows at least two separate
'Get Quotes' buttons (tab and submit), making the locator inherently order-dependent.

examples/ui/tests/cross-chain.spec.ts[109-123]
examples/ui/app/cross-chain/components/SwapForm.tsx[30-33]
examples/ui/app/cross-chain/components/SwapForm.tsx[260-266]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Playwright test uses an ambiguous locator for 'Get Quotes' and disambiguates with `.last()`. Because the UI has multiple 'Get Quotes' buttons (mode tab + submit), this can break when markup/order changes.

## Issue Context
`SwapForm` defines a tab option labeled 'Get Quotes' and also renders a submit button whose text is 'Get Quotes' in get-quotes mode.

## Fix Focus Areas
- examples/ui/tests/cross-chain.spec.ts[109-126]
- examples/ui/app/cross-chain/components/SwapForm.tsx[30-33]
- examples/ui/app/cross-chain/components/SwapForm.tsx[260-266]

## Suggested fix
Replace `getByRole('button', { name: 'Get Quotes' }).last()` with a stable selector for the submit action, e.g. `page.getByTestId('submit-button').click()` or `page.locator('button[type="submit"]').click()`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Missing E2E key validation 🐞 Bug ☼ Reliability
Description
The E2E connector silently falls back to '' when NEXT_PUBLIC_E2E_PRIVATE_KEY is unset, which can
lead to opaque E2E failures (no connected address, inability to submit/execute) instead of a clear
configuration error. E2E mode should fail fast when the key is missing/invalid and avoid
constructing the E2E provider when not in E2E mode.
Code

examples/ui/app/cross-chain/config/e2eConnector.ts[10]

+  account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',
Evidence
The E2E provider is configured to use an empty string when the key is missing, while the UI logic
requires a connected address for recipient autofill and for form submission to proceed; this
combination makes missing-key scenarios fail indirectly rather than explicitly.

examples/ui/app/cross-chain/config/e2eConnector.ts[7-11]
examples/ui/app/cross-chain/config/publicClient.ts[5-10]
examples/ui/app/cross-chain/components/SwapForm.tsx[151-159]
examples/ui/app/cross-chain/components/SwapForm.tsx[174-185]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The E2E provider is configured with `account: (...) || ''`, so a missing env var becomes an empty string account. This risks unclear runtime behavior during E2E (e.g., wallet appears disconnected / no sender address) rather than a clear error.

## Issue Context
E2E mode is enabled via `NEXT_PUBLIC_E2E === 'true'`, and the UI depends on a connected address for auto-fill and submission.

## Fix
- If `isE2E` is true, validate `NEXT_PUBLIC_E2E_PRIVATE_KEY` is present and looks like a hex private key; otherwise throw an explicit error early.
- Avoid creating the E2E provider at module init when not in E2E mode (e.g., wrap creation in a function that’s only called when `isE2E` is true).

## Fix Focus Areas
- examples/ui/app/cross-chain/config/e2eConnector.ts[7-17]
- examples/ui/app/cross-chain/config/publicClient.ts[5-10]
- examples/ui/app/cross-chain/config/wagmi.ts[1-38]
- examples/ui/app/cross-chain/components/SwapForm.tsx[174-185]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
6. Hardcoded E2E wallet address 🐞 Bug ☼ Reliability
Description
Playwright assertions hardcode the connected wallet address, but that address is derived from the
configured E2E private key; rotating/replacing the CI secret will change the address and break these
tests. This will cause avoidable CI failures whenever the key is rotated (which is expected for a
dedicated E2E account).
Code

examples/ui/tests/cross-chain.spec.ts[R30-34]

  test('auto-fills with connected address on load', async ({ page }) => {
    const recipientInput = page.getByRole('textbox', { name: 'Recipient Address' });
-    await expect(recipientInput).toHaveValue('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266');
+
+    await expect(recipientInput).toHaveValue('0xc59c92D9d6064464280B621C42A6ECDa0EA2D29b');
  });
Evidence
The tests assert a specific address string, while the UI sets the recipient to the connected wallet
address (from wagmi). That connected address depends on the E2E provider configuration, which is
derived from the E2E private key.

examples/ui/tests/cross-chain.spec.ts[29-34]
examples/ui/tests/cross-chain.spec.ts[192-199]
examples/ui/app/cross-chain/components/SwapForm.tsx[63-66]
examples/ui/app/cross-chain/components/SwapForm.tsx[151-159]
examples/ui/app/cross-chain/config/e2eConnector.ts[7-11]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The E2E tests assert a fixed wallet address value. Since the connected address comes from the configured E2E private key, any key rotation will change the address and make tests fail.

## Issue Context
The UI auto-fills the recipient field from the connected wagmi address, so the test is implicitly tied to the walletless provider’s configured key.

## Fix
Update tests to avoid hardcoding the address:
- Compute the expected address from the private key at test runtime (e.g., using `viem/accounts` `privateKeyToAccount`) **or**
- Add a separate `NEXT_PUBLIC_E2E_ADDRESS` CI secret/env and assert against that, keeping the private key out of assertions.

Apply the change to both the recipient autofill assertion and the clipboard assertion.

## Fix Focus Areas
- examples/ui/tests/cross-chain.spec.ts[29-34]
- examples/ui/tests/cross-chain.spec.ts[192-199]
- examples/ui/app/cross-chain/components/SwapForm.tsx[63-66]
- examples/ui/app/cross-chain/components/SwapForm.tsx[151-159]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

  • Author self-review: I have reviewed the code review findings, and addressed the relevant ones.

Previous review results

Review updated until commit 56ecea2

Results up to commit 9dd725d


🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Client-bundled private key 🐞 Bug ⛨ Security
Description
The cross-chain E2E connector reads process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY in client-side code,
so any build produced with that env set will ship the private key to the browser runtime where it
can be extracted. CI workflows also inject this secret into build/test steps, increasing the chance
of accidental exposure via build artifacts/logging/misdeployment.
Code

examples/ui/app/cross-chain/config/e2eConnector.ts[10]

+  account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',
Evidence
The app constructs an E2E signing provider from process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY
(client-side), and CI workflows explicitly inject that secret into build/test environments, meaning
the key is expected to be available in runtime where browser code can access it.

examples/ui/app/cross-chain/config/e2eConnector.ts[7-17]
.github/workflows/build-lint.yml[34-39]
.github/workflows/test-e2e.yml[43-49]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`NEXT_PUBLIC_E2E_PRIVATE_KEY` is consumed in client-side code to create the walletless E2E provider. If that env var is present during a build that is deployed/shared, the resulting client bundle/runtime contains the private key and it can be extracted.

## Issue Context
This PR also passes `NEXT_PUBLIC_E2E_PRIVATE_KEY` into CI build/test workflows, making it more likely the key ends up embedded in built assets.

## Fix
Refactor so the private key is not part of the Next.js client bundle:
- Do **not** use a `NEXT_PUBLIC_*` env var for a private key.
- Prefer passing the key only to the Playwright runner and injecting it at runtime (e.g., `page.addInitScript(...)` to set a global like `globalThis.__E2E_PRIVATE_KEY`, then have the E2E connector read that global only when `NEXT_PUBLIC_E2E === 'true'`).
- Remove passing the secret to non-E2E build steps unless strictly required.

## Fix Focus Areas
- examples/ui/app/cross-chain/config/e2eConnector.ts[1-26]
- .github/workflows/build-lint.yml[34-39]
- .github/workflows/test-e2e.yml[43-49]
- examples/ui/playwright.config.ts[1-36]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Hardcoded E2E wallet address 🐞 Bug ☼ Reliability
Description
Playwright assertions hardcode the connected wallet address, but that address is derived from the
configured E2E private key; rotating/replacing the CI secret will change the address and break these
tests. This will cause avoidable CI failures whenever the key is rotated (which is expected for a
dedicated E2E account).
Code

examples/ui/tests/cross-chain.spec.ts[R30-34]

  test('auto-fills with connected address on load', async ({ page }) => {
    const recipientInput = page.getByRole('textbox', { name: 'Recipient Address' });
-    await expect(recipientInput).toHaveValue('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266');
+
+    await expect(recipientInput).toHaveValue('0xc59c92D9d6064464280B621C42A6ECDa0EA2D29b');
  });
Evidence
The tests assert a specific address string, while the UI sets the recipient to the connected wallet
address (from wagmi). That connected address depends on the E2E provider configuration, which is
derived from the E2E private key.

examples/ui/tests/cross-chain.spec.ts[29-34]
examples/ui/tests/cross-chain.spec.ts[192-199]
examples/ui/app/cross-chain/components/SwapForm.tsx[63-66]
examples/ui/app/cross-chain/components/SwapForm.tsx[151-159]
examples/ui/app/cross-chain/config/e2eConnector.ts[7-11]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The E2E tests assert a fixed wallet address value. Since the connected address comes from the configured E2E private key, any key rotation will change the address and make tests fail.

## Issue Context
The UI auto-fills the recipient field from the connected wagmi address, so the test is implicitly tied to the walletless provider’s configured key.

## Fix
Update tests to avoid hardcoding the address:
- Compute the expected address from the private key at test runtime (e.g., using `viem/accounts` `privateKeyToAccount`) **or**
- Add a separate `NEXT_PUBLIC_E2E_ADDRESS` CI secret/env and assert against that, keeping the private key out of assertions.

Apply the change to both the recipient autofill assertion and the clipboard assertion.

## Fix Focus Areas
- examples/ui/tests/cross-chain.spec.ts[29-34]
- examples/ui/tests/cross-chain.spec.ts[192-199]
- examples/ui/app/cross-chain/components/SwapForm.tsx[63-66]
- examples/ui/app/cross-chain/components/SwapForm.tsx[151-159]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Missing E2E key validation 🐞 Bug ☼ Reliability
Description
The E2E connector silently falls back to '' when NEXT_PUBLIC_E2E_PRIVATE_KEY is unset, which can
lead to opaque E2E failures (no connected address, inability to submit/execute) instead of a clear
configuration error. E2E mode should fail fast when the key is missing/invalid and avoid
constructing the E2E provider when not in E2E mode.
Code

examples/ui/app/cross-chain/config/e2eConnector.ts[10]

+  account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',
Evidence
The E2E provider is configured to use an empty string when the key is missing, while the UI logic
requires a connected address for recipient autofill and for form submission to proceed; this
combination makes missing-key scenarios fail indirectly rather than explicitly.

examples/ui/app/cross-chain/config/e2eConnector.ts[7-11]
examples/ui/app/cross-chain/config/publicClient.ts[5-10]
examples/ui/app/cross-chain/components/SwapForm.tsx[151-159]
examples/ui/app/cross-chain/components/SwapForm.tsx[174-185]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The E2E provider is configured with `account: (...) || ''`, so a missing env var becomes an empty string account. This risks unclear runtime behavior during E2E (e.g., wallet appears disconnected / no sender address) rather than a clear error.

## Issue Context
E2E mode is enabled via `NEXT_PUBLIC_E2E === 'true'`, and the UI depends on a connected address for auto-fill and submission.

## Fix
- If `isE2E` is true, validate `NEXT_PUBLIC_E2E_PRIVATE_KEY` is present and looks like a hex private key; otherwise throw an explicit error early.
- Avoid creating the E2E provider at module init when not in E2E mode (e.g., wrap creation in a function that’s only called when `isE2E` is true).

## Fix Focus Areas
- examples/ui/app/cross-chain/config/e2eConnector.ts[7-17]
- examples/ui/app/cross-chain/config/publicClient.ts[5-10]
- examples/ui/app/cross-chain/config/wagmi.ts[1-38]
- examples/ui/app/cross-chain/components/SwapForm.tsx[174-185]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Results up to commit 23b58af


🐞 Bugs (3) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Unmocked OIF stalls E2E 🐞 Bug ☼ Reliability
Description
cross-chain.spec.ts no longer aborts OIF network traffic, but the app still instantiates the OIF
provider and eagerly prefetches discoverAssets(), making page readiness dependent on
https://oif-api.openzeppelin.com/api latency/availability and causing Playwright timeouts/flakes.
This can impact multiple tests because the warm-cache prefetch happens as soon as an executor is
created, not only when OIF is explicitly selected.
Code

examples/ui/tests/cross-chain.spec.ts[R3-5]

+test.beforeEach(async ({ page }) => {
  await page.goto('/cross-chain?testnet=true');
});
Evidence
The tests now only navigate to the page, while the app code still constructs an OIF provider
pointing at the OIF API URL and prefetches discovery on executor creation, which can introduce
external-call latency into test startup.

examples/ui/tests/cross-chain.spec.ts[1-5]
examples/ui/app/cross-chain/services/sdk.ts[17-18]
examples/ui/app/cross-chain/services/sdk.ts[58-66]
examples/ui/app/cross-chain/services/sdk.ts[88-97]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The E2E suite removed the request interception that previously aborted OIF API calls, but the app still constructs an OIF provider and prefetches discovery. This makes E2E runs sensitive to OIF endpoint slowness/outages, leading to long hangs and flaky timeouts.

## Issue Context
Even if tests primarily exercise Across/Relay/LI.FI, `getExecutor()` prefetches discovery for both executor instances, which can trigger OIF network traffic.

## Fix Focus Areas
- examples/ui/tests/cross-chain.spec.ts[1-6]
- examples/ui/app/cross-chain/services/sdk.ts[17-97]

## Suggested fix
Reintroduce a fast-fail route abort for OIF in Playwright (e.g., `context.route('**/oif-api.openzeppelin.com/**', route => route.abort('blockedbyclient'))`), or add an E2E-only switch in `buildExecutor()` to skip adding the OIF provider during E2E runs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Stale Anvil E2E docs 🐞 Bug ⚙ Maintainability
Description
Playwright no longer starts an Anvil fork and .env.e2e no longer contains NEXT_PUBLIC_ANVIL_*,
but the README, .env.example, and publicClient.ts still describe/consume Anvil settings, leaving
contributors with incorrect E2E setup instructions. This configuration drift increases the chance of
broken local runs and confusing future maintenance (e.g., someone sets NEXT_PUBLIC_ANVIL_URL
expecting it to be used).
Code

examples/ui/playwright.config.ts[R28-31]

  webServer: [
-    {
-      command: 'node ./scripts/start-anvil-fork.mjs',
-      port: anvilPort,
-      env: { ANVIL_PORT: String(anvilPort) },
-      reuseExistingServer: !process.env.CI,
-      timeout: 60_000,
-    },
    {
      command: 'pnpm build && pnpm start',
      url: 'http://localhost:3000',
Evidence
Playwright is configured to only build/start the app (no Anvil), .env.e2e contains only
NEXT_PUBLIC_E2E=true, while docs and runtime config still reference Anvil env vars and Anvil-based
boot flow.

examples/ui/playwright.config.ts[28-35]
examples/ui/.env.e2e[1-1]
examples/ui/README.md[30-44]
examples/ui/.env.example[8-16]
examples/ui/app/cross-chain/config/publicClient.ts[5-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR removes the Anvil webServer setup and Anvil env vars from `.env.e2e`, but documentation and runtime config still reference Anvil. This creates misleading setup guidance and leftover configuration paths.

## Issue Context
README and `.env.example` still instruct users that Playwright boots an anvil fork and list Anvil-related env vars; `publicClient.ts` still reads `NEXT_PUBLIC_ANVIL_URL` / `NEXT_PUBLIC_ANVIL_CHAIN_ID`.

## Fix Focus Areas
- examples/ui/playwright.config.ts[1-35]
- examples/ui/.env.e2e[1-1]
- examples/ui/README.md[19-44]
- examples/ui/.env.example[8-16]
- examples/ui/app/cross-chain/config/publicClient.ts[5-10]

## Suggested fix
Update README and `.env.example` to reflect the new real-RPC E2E flow and document `NEXT_PUBLIC_E2E_PRIVATE_KEY` usage; either remove the `NEXT_PUBLIC_ANVIL_*` branch from `publicClient.ts` or clearly mark it deprecated/unused and ensure it cannot be accidentally relied upon.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Brittle .last() selector 🐞 Bug ☼ Reliability
Description
The cross-chain execution test clicks getByRole('button', { name: 'Get Quotes' }).last(), but the
UI also renders a separate 'Get Quotes' button as the mode tab label, making the locator dependent
on DOM ordering and potentially clicking the tab instead of the submit button. This can cause the
test to not fetch quotes and fail downstream when provider buttons/Execute never appear.
Code

examples/ui/tests/cross-chain.spec.ts[R117-123]

+    await page.getByRole('textbox', { name: 'Amount' }).fill('0.03');
+    await page.getByRole('button', { name: 'Get Quotes' }).last().click();
+    await page
+      .locator('button')
+      .filter({ hasText: /Relay/ })
+      .click();
+    await page.getByRole('button', { name: 'Execute' }).click();
Evidence
The test uses .last() on a duplicated accessible name, and the UI code shows at least two separate
'Get Quotes' buttons (tab and submit), making the locator inherently order-dependent.

examples/ui/tests/cross-chain.spec.ts[109-123]
examples/ui/app/cross-chain/components/SwapForm.tsx[30-33]
examples/ui/app/cross-chain/components/SwapForm.tsx[260-266]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Playwright test uses an ambiguous locator for 'Get Quotes' and disambiguates with `.last()`. Because the UI has multiple 'Get Quotes' buttons (mode tab + submit), this can break when markup/order changes.

## Issue Context
`SwapForm` defines a tab option labeled 'Get Quotes' and also renders a submit button whose text is 'Get Quotes' in get-quotes mode.

## Fix Focus Areas
- examples/ui/tests/cross-chain.spec.ts[109-126]
- examples/ui/app/cross-chain/components/SwapForm.tsx[30-33]
- examples/ui/app/cross-chain/components/SwapForm.tsx[260-266]

## Suggested fix
Replace `getByRole('button', { name: 'Get Quotes' }).last()` with a stable selector for the submit action, e.g. `page.getByTestId('submit-button').click()` or `page.locator('button[type="submit"]').click()`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

const e2eTestProvider = createE2EProvider({
chains: ALL_CHAINS,
rpcUrls,
account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Client-bundled private key 🐞 Bug ⛨ Security

The cross-chain E2E connector reads process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY in client-side code,
so any build produced with that env set will ship the private key to the browser runtime where it
can be extracted. CI workflows also inject this secret into build/test steps, increasing the chance
of accidental exposure via build artifacts/logging/misdeployment.
Agent Prompt
## Issue description
`NEXT_PUBLIC_E2E_PRIVATE_KEY` is consumed in client-side code to create the walletless E2E provider. If that env var is present during a build that is deployed/shared, the resulting client bundle/runtime contains the private key and it can be extracted.

## Issue Context
This PR also passes `NEXT_PUBLIC_E2E_PRIVATE_KEY` into CI build/test workflows, making it more likely the key ends up embedded in built assets.

## Fix
Refactor so the private key is not part of the Next.js client bundle:
- Do **not** use a `NEXT_PUBLIC_*` env var for a private key.
- Prefer passing the key only to the Playwright runner and injecting it at runtime (e.g., `page.addInitScript(...)` to set a global like `globalThis.__E2E_PRIVATE_KEY`, then have the E2E connector read that global only when `NEXT_PUBLIC_E2E === 'true'`).
- Remove passing the secret to non-E2E build steps unless strictly required.

## Fix Focus Areas
- examples/ui/app/cross-chain/config/e2eConnector.ts[1-26]
- .github/workflows/build-lint.yml[34-39]
- .github/workflows/test-e2e.yml[43-49]
- examples/ui/playwright.config.ts[1-36]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="examples/ui/app/cross-chain/config/e2eConnector.ts">

<violation number="1" location="examples/ui/app/cross-chain/config/e2eConnector.ts:10">
P1: Using `NEXT_PUBLIC_` prefix for a private key causes Next.js to inline it into the client-side JavaScript bundle at build time. Any visitor to a deployed build can extract it from the static JS assets. Use a non-prefixed env var and inject it only at test runtime (e.g., via Playwright's `page.addInitScript` or a server-only route) to keep the key out of client bundles.</violation>

<violation number="2" location="examples/ui/app/cross-chain/config/e2eConnector.ts:10">
P2: Custom agent: **TypeScript & React Standards**

Missing runtime validation for NEXT_PUBLIC_E2E_PRIVATE_KEY env variable; a compile-time `as` assertion provides no runtime guarantees. Use Zod (or equivalent) to validate the env var and fail fast with a clear error.</violation>
</file>

<file name=".github/workflows/build-lint.yml">

<violation number="1" location=".github/workflows/build-lint.yml:38">
P1: Private key is passed as a `NEXT_PUBLIC_*` build env, which exposes it to client bundles. Use a server-only env name and keep signing key access out of browser-facing code/build-time public envs.</violation>
</file>

<file name="examples/ui/tests/cross-chain.spec.ts">

<violation number="1" location="examples/ui/tests/cross-chain.spec.ts:33">
P2: Tests hard-code a specific wallet address even though the E2E signer is env-configured and rotatable, making CI brittle on key rotation.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread examples/ui/app/cross-chain/config/e2eConnector.ts Outdated
const e2eTestProvider = createE2EProvider({
chains: ALL_CHAINS,
rpcUrls,
account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',

@cubic-dev-ai cubic-dev-ai Bot Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Using NEXT_PUBLIC_ prefix for a private key causes Next.js to inline it into the client-side JavaScript bundle at build time. Any visitor to a deployed build can extract it from the static JS assets. Use a non-prefixed env var and inject it only at test runtime (e.g., via Playwright's page.addInitScript or a server-only route) to keep the key out of client bundles.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/ui/app/cross-chain/config/e2eConnector.ts, line 10:

<comment>Using `NEXT_PUBLIC_` prefix for a private key causes Next.js to inline it into the client-side JavaScript bundle at build time. Any visitor to a deployed build can extract it from the static JS assets. Use a non-prefixed env var and inject it only at test runtime (e.g., via Playwright's `page.addInitScript` or a server-only route) to keep the key out of client bundles.</comment>

<file context>
@@ -7,6 +7,7 @@ import type { Chain } from 'viem/chains';
 const e2eTestProvider = createE2EProvider({
   chains: ALL_CHAINS,
   rpcUrls,
+  account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',
 });
 
</file context>
Fix with cubic

run: pnpm build
env:
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
NEXT_PUBLIC_E2E_PRIVATE_KEY: ${{ secrets.NEXT_PUBLIC_E2E_PRIVATE_KEY }}

@cubic-dev-ai cubic-dev-ai Bot Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Private key is passed as a NEXT_PUBLIC_* build env, which exposes it to client bundles. Use a server-only env name and keep signing key access out of browser-facing code/build-time public envs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/build-lint.yml, line 38:

<comment>Private key is passed as a `NEXT_PUBLIC_*` build env, which exposes it to client bundles. Use a server-only env name and keep signing key access out of browser-facing code/build-time public envs.</comment>

<file context>
@@ -35,6 +35,7 @@ jobs:
               run: pnpm build
               env:
                   MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
+                  NEXT_PUBLIC_E2E_PRIVATE_KEY: ${{ secrets.NEXT_PUBLIC_E2E_PRIVATE_KEY }}
 
             - name: Check types
</file context>
Fix with cubic

const e2eTestProvider = createE2EProvider({
chains: ALL_CHAINS,
rpcUrls,
account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',

@cubic-dev-ai cubic-dev-ai Bot Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Custom agent: TypeScript & React Standards

Missing runtime validation for NEXT_PUBLIC_E2E_PRIVATE_KEY env variable; a compile-time as assertion provides no runtime guarantees. Use Zod (or equivalent) to validate the env var and fail fast with a clear error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/ui/app/cross-chain/config/e2eConnector.ts, line 10:

<comment>Missing runtime validation for NEXT_PUBLIC_E2E_PRIVATE_KEY env variable; a compile-time `as` assertion provides no runtime guarantees. Use Zod (or equivalent) to validate the env var and fail fast with a clear error.</comment>

<file context>
@@ -7,6 +7,7 @@ import type { Chain } from 'viem/chains';
 const e2eTestProvider = createE2EProvider({
   chains: ALL_CHAINS,
   rpcUrls,
+  account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',
 });
 
</file context>
Fix with cubic

const recipientInput = page.getByRole('textbox', { name: 'Recipient Address' });
await expect(recipientInput).toHaveValue('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266');

await expect(recipientInput).toHaveValue('0xc59c92D9d6064464280B621C42A6ECDa0EA2D29b');

@cubic-dev-ai cubic-dev-ai Bot Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Tests hard-code a specific wallet address even though the E2E signer is env-configured and rotatable, making CI brittle on key rotation.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/ui/tests/cross-chain.spec.ts, line 33:

<comment>Tests hard-code a specific wallet address even though the E2E signer is env-configured and rotatable, making CI brittle on key rotation.</comment>

<file context>
@@ -123,36 +24,13 @@ test.describe('Asset Discovery', () => {
     const recipientInput = page.getByRole('textbox', { name: 'Recipient Address' });
-    await expect(recipientInput).toHaveValue('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266');
+
+    await expect(recipientInput).toHaveValue('0xc59c92D9d6064464280B621C42A6ECDa0EA2D29b');
   });
 
</file context>
Fix with cubic

Comment on lines +3 to 5
test.beforeEach(async ({ page }) => {
await page.goto('/cross-chain?testnet=true');
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Unmocked oif stalls e2e 🐞 Bug ☼ Reliability

cross-chain.spec.ts no longer aborts OIF network traffic, but the app still instantiates the OIF
provider and eagerly prefetches discoverAssets(), making page readiness dependent on
https://oif-api.openzeppelin.com/api latency/availability and causing Playwright timeouts/flakes.
This can impact multiple tests because the warm-cache prefetch happens as soon as an executor is
created, not only when OIF is explicitly selected.
Agent Prompt
## Issue description
The E2E suite removed the request interception that previously aborted OIF API calls, but the app still constructs an OIF provider and prefetches discovery. This makes E2E runs sensitive to OIF endpoint slowness/outages, leading to long hangs and flaky timeouts.

## Issue Context
Even if tests primarily exercise Across/Relay/LI.FI, `getExecutor()` prefetches discovery for both executor instances, which can trigger OIF network traffic.

## Fix Focus Areas
- examples/ui/tests/cross-chain.spec.ts[1-6]
- examples/ui/app/cross-chain/services/sdk.ts[17-97]

## Suggested fix
Reintroduce a fast-fail route abort for OIF in Playwright (e.g., `context.route('**/oif-api.openzeppelin.com/**', route => route.abort('blockedbyclient'))`), or add an E2E-only switch in `buildExecutor()` to skip adding the OIF provider during E2E runs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 23b58af

@qodo-code-review

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 56ecea2

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="examples/ui/app/cross-chain/config/e2eConnector.ts">

<violation number="1" location="examples/ui/app/cross-chain/config/e2eConnector.ts:10">
P1: Using a `NEXT_PUBLIC_` prefix for a private key causes Next.js to inline the value into the client-side JavaScript bundle at build time. Anyone inspecting the browser bundle can extract the key. Use a non-prefixed env var and inject the key at runtime (e.g., via Playwright's `page.addInitScript`) instead of baking it into the build output.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

const e2eTestProvider = createE2EProvider({
chains: ALL_CHAINS,
rpcUrls,
account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || undefined,

@cubic-dev-ai cubic-dev-ai Bot Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Using a NEXT_PUBLIC_ prefix for a private key causes Next.js to inline the value into the client-side JavaScript bundle at build time. Anyone inspecting the browser bundle can extract the key. Use a non-prefixed env var and inject the key at runtime (e.g., via Playwright's page.addInitScript) instead of baking it into the build output.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/ui/app/cross-chain/config/e2eConnector.ts, line 10:

<comment>Using a `NEXT_PUBLIC_` prefix for a private key causes Next.js to inline the value into the client-side JavaScript bundle at build time. Anyone inspecting the browser bundle can extract the key. Use a non-prefixed env var and inject the key at runtime (e.g., via Playwright's `page.addInitScript`) instead of baking it into the build output.</comment>

<file context>
@@ -7,7 +7,7 @@ import type { Chain } from 'viem/chains';
   chains: ALL_CHAINS,
   rpcUrls,
-  account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || '',
+  account: (process.env.NEXT_PUBLIC_E2E_PRIVATE_KEY as `0x${string}`) || undefined,
 });
 
</file context>
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant