Mock wallet connector ships a hardcoded private key: NEXT_PUBLIC_MOCK_WALLET=true signs real-looking transactions
Labels / Complexity: bug · security · Medium Complexity — Medium
Problem
src/config/mockConnector.ts creates a wagmi connector from a hardcoded private key committed to the repository:
const account = privateKeyToAccount(
"0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
);
and src/config/wagmi.ts (line 120) swaps it into the connector list whenever NEXT_PUBLIC_MOCK_WALLET === "true":
const connectors =
process.env.NEXT_PUBLIC_MOCK_WALLET === "true"
? [mockConnector]
: [injected()];
The connector also reports isAuthorized() { return true; } unconditionally. Consequences:
- Any build with the flag set signs with a publicly-known key. If
NEXT_PUBLIC_MOCK_WALLET=true is ever set in a staging or production-like environment — or copied from a demo config — every transaction the UI "confirms" is signed with 0x4f3e…b1d, which anyone can now sign as. The key's address is public and the key is burned.
- The mock is indistinguishable from a real connection.
useAuth/useWalletConnector flows treat the mock account as a connected wallet, so demo-mode code paths look production-correct while being unsafe.
Root cause
src/config/mockConnector.ts (hardcoded privateKeyToAccount(...) key) and src/config/wagmi.ts line 120 (env-flag activation without a NODE_ENV guard).
Why this is architecturally hard
- Mocking must be scoped, not flagged. The correct design keeps the mock out of production builds entirely (e.g.
NODE_ENV !== "production" AND the flag, or a separate entry point). A contributor must decide the guard and prove the mock cannot activate in a production build (next build output check or a test that asserts the connector list in production mode).
- The committed key must be treated as compromised. The key's address should never be funded on any real network, and the mock should generate a random key per session (or use
viem's generatePrivateKey) instead of a fixed one, so no long-lived secret exists to leak.
Acceptance criteria
- The mock connector cannot activate when
NODE_ENV === "production" (or equivalent build-time guard), and a test asserts the production connector list contains no mock.
- The hardcoded private key is removed; the mock (if kept for local dev) uses a generated per-session key.
- Documentation (env example/README) states that
NEXT_PUBLIC_MOCK_WALLET is dev-only.
npm run typecheck, npm test, and npm run lint pass.
Out of scope
Redesigning the wallet-connection UI is out of scope; this issue is the mock's key and activation scope.
Getting started
src/config/mockConnector.ts — the hardcoded key and isAuthorized override
src/config/wagmi.ts — the connector selection at line 120
Commands: npm run typecheck, npm test, npm run lint, npm run build.
Good first files to read: src/config/mockConnector.ts, src/config/wagmi.ts.
Mock wallet connector ships a hardcoded private key: NEXT_PUBLIC_MOCK_WALLET=true signs real-looking transactions
Labels / Complexity: bug · security · Medium Complexity — Medium
Problem
src/config/mockConnector.tscreates a wagmi connector from a hardcoded private key committed to the repository:and
src/config/wagmi.ts(line 120) swaps it into the connector list wheneverNEXT_PUBLIC_MOCK_WALLET === "true":The connector also reports
isAuthorized() { return true; }unconditionally. Consequences:NEXT_PUBLIC_MOCK_WALLET=trueis ever set in a staging or production-like environment — or copied from a demo config — every transaction the UI "confirms" is signed with0x4f3e…b1d, which anyone can now sign as. The key's address is public and the key is burned.useAuth/useWalletConnectorflows treat the mock account as a connected wallet, so demo-mode code paths look production-correct while being unsafe.Root cause
src/config/mockConnector.ts(hardcodedprivateKeyToAccount(...)key) andsrc/config/wagmi.tsline 120 (env-flag activation without aNODE_ENVguard).Why this is architecturally hard
NODE_ENV !== "production"AND the flag, or a separate entry point). A contributor must decide the guard and prove the mock cannot activate in a production build (next buildoutput check or a test that asserts the connector list in production mode).viem'sgeneratePrivateKey) instead of a fixed one, so no long-lived secret exists to leak.Acceptance criteria
NODE_ENV === "production"(or equivalent build-time guard), and a test asserts the production connector list contains no mock.NEXT_PUBLIC_MOCK_WALLETis dev-only.npm run typecheck,npm test, andnpm run lintpass.Out of scope
Redesigning the wallet-connection UI is out of scope; this issue is the mock's key and activation scope.
Getting started
src/config/mockConnector.ts— the hardcoded key andisAuthorizedoverridesrc/config/wagmi.ts— the connector selection at line 120Commands:
npm run typecheck,npm test,npm run lint,npm run build.Good first files to read:
src/config/mockConnector.ts,src/config/wagmi.ts.