This project is a demo demonstrating how to integrate the Linera Web client with Dynamic (wallet auth) in a Next.js app.
Based on the Linera Web hosted example: hosted-counter-metamask, adapted to use Dynamic and Next.js.
- Loading the Linera Web WASM bundle from
public
with the headers required for SharedArrayBuffer and workers - Using Dynamic for wallet connection and a custom Signer bridge so Linera can request signatures from the connected wallet
- Small glue code to make Linera’s worker module resolve correctly inside a Next.js environment
next.config.ts
: sets COEP/COOP response headers required by Linera Webpublic/js/@linera/client/
: Linera assets are copied here and served directly frompublic
app/layout.tsx
: injects a small iframe shim required by Dynamic under these headerslib/dynamic-signer.ts
: implements Linera’sSigner
interface using the connected Dynamic walletpublic/js/@linera/client/
: the Linera Web bundle (JS, WASM, and worker snippets)
- Node 18+
- pnpm (recommended)
- Optional (only if rebuilding Linera Web locally): Rust toolchain and whatever
linera-protocol/linera-web
requires
- Clone and initialize the Linera submodule
git clone <this-repo>
cd linera-demo
This project depends on linera-protocol
via a git submodule located at linera-protocol/
.
- First-time initialization:
git submodule init
- Update to the latest upstream version:
git submodule update --remote
- Provide the Dynamic environment ID
Create .env.local
with:
NEXT_PUBLIC_DYNAMIC_ENV_ID=your_dynamic_environment_id
- Install and run the app
pnpm install
pnpm dev
Open http://localhost:3000.
-
Linera Web assets are copied into
public/js/@linera/client/
and served as static files by Next.js -
The app sets response headers via
next.config.ts
to enable COEP/COOP for Linera Web:Cross-Origin-Embedder-Policy: credentialless
Cross-Origin-Opener-Policy: same-origin
-
Dynamic is initialized in
lib/providers.tsx
viaDynamicContextProvider
. The signer bridge inlib/dynamic-signer.ts
implements Linera’sSigner
by delegating to the connected wallet via EIP-191personal_sign
. -
The small
dynamic-iframe-shim.js
is loaded inapp/layout.tsx
before interactive scripts. This marks Dynamic-hosted iframes ascredentialless
, which is required when COEP/COOP are enabled. -
Optionally,
app/components/WasmPreload.tsx
shows how to preload the Linera JS/WASM for faster startup.
-
Dynamic iframe + COEP/COOP headers
- Because Linera requires
Cross-Origin-Embedder-Policy: credentialless
andCross-Origin-Opener-Policy: same-origin
, third‑party iframes need to be markedcredentialless
too. We add a small shim loaded inapp/layout.tsx
: - File:
public/js/dynamic-iframe-shim.js
(referenced inapp/layout.tsx
)
- Because Linera requires
-
Do not call
dynamicWallet.signMessage(msgHex)
- The value passed from Linera is already pre-hashed; using a typical
signMessage
flow will hash it again. Inlib/dynamic-signer.ts
we instead call the wallet client’s JSON‑RPC directly withpersonal_sign
and the message hex.
- The value passed from Linera is already pre-hashed; using a typical
-
Linera worker module URL must be absolute
- The generated worker snippet may embed a
ROOT
/relative URL at build time. In this POC we explicitly set the worker to import from the Next.jspublic
path so it resolves correctly: - File:
public/js/@linera/client/snippets/.../web_worker_module.js
→const mainModuleUrl = "/js/@linera/client/linera_web.js";
- The generated worker snippet may embed a
pnpm dev # start Next.js
pnpm build # production build
pnpm start # run production build