diff --git a/frontend/src/lib/network.ts b/frontend/src/lib/network.ts index 5976109..e8b43ab 100644 --- a/frontend/src/lib/network.ts +++ b/frontend/src/lib/network.ts @@ -10,3 +10,19 @@ export const USDX_ISSUER = import.meta.env.VITE_USDX_ISSUER ?? ""; export const POOL_ID = import.meta.env.VITE_POOL_ID ?? ""; export const isConfigured = Boolean(CBRL_ISSUER && USDX_ISSUER); + +/** + * A hard guard, mirroring the backend's `assertTestnet` (src/lib/network.ts): this dApp is + * testnet-only. It signs and submits destructive, irreversible ops (freeze, clawback, lock), + * so it must never operate against the public network. Returns an error message when the + * configured passphrase is PUBLIC so the caller can fail visibly instead of silently signing. + */ +export function testnetGuardError(): string | null { + if (NETWORK_PASSPHRASE === Networks.PUBLIC) { + return ( + "Stellar Mint is a testnet teaching dApp. It refuses to run against the PUBLIC network. " + + "Set VITE_NETWORK_PASSPHRASE to the testnet passphrase." + ); + } + return null; +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 5662951..0861fb0 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,15 +3,31 @@ import { createRoot } from "react-dom/client"; import { Buffer } from "buffer"; import { StoreProvider } from "./store.js"; import App from "./App.js"; +import { testnetGuardError } from "./lib/network.js"; import "./index.css"; // stellar-sdk expects Node's Buffer in the browser. globalThis.Buffer = globalThis.Buffer ?? Buffer; -createRoot(document.getElementById("root")!).render( - - - - - , -); +const root = createRoot(document.getElementById("root")!); + +// Hard guard: testnet-only, mirroring the backend's assertTestnet. Fail visibly before +// mounting the app so a misconfigured PUBLIC passphrase can never sign a transaction. +const guardError = testnetGuardError(); +if (guardError) { + root.render( +
+
+ {guardError} +
+
, + ); +} else { + root.render( + + + + + , + ); +}