This project demonstrates how to integrate the Linera Web client with Dynamic wallet authentication in a Vite + React application. It showcases a complete flow from wallet connection through chain creation to application interaction.
The demo implements a simple counter application that runs on the Linera network, allowing users to connect their Dynamic wallet, create a personal chain, deploy the counter application, and interact with it through GraphQL queries and mutations.
Based on the Linera Web hosted example "hosted-counter-metamask," adapted to use Dynamic wallet authentication and Vite as the build tool.
- Complete wallet authentication flow using Dynamic SDK
- Linera Web JS/WASM bundle loading under COOP/COEP headers for cross-origin isolation
- Custom Signer bridge implementation that connects Dynamic wallets to Linera's signing requirements (EIP-191
personal_sign
) - Chain creation and application deployment on the Linera network
- GraphQL integration for querying and mutating application state
- Real-time block subscription and notification handling
- Vite configuration optimized for Linera Web integration
- Dynamic iframe shim for handling wallet connection UI under strict CORS policies
vite.config.ts
: sets response headers required by Linera WebCross-Origin-Embedder-Policy: credentialless
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: cross-origin
index.html
: injects a small iframe shim required by Dynamic under these headerspublic/js/dynamic-iframe-shim.js
: marks Dynamic-hosted iframes as credentiallesssrc/lib/dynamic-signer.ts
: implements Linera’sSigner
interface using the connected Dynamic wallet (EIP-191personal_sign
)src/lib/linera-adapter.ts
: connects to the Linera faucet, creates/claims a chain, and exposes query/mutation helperssrc/constants.ts
: setLINERA_RPC_URL
and theCOUNTER_APP_ID
used by the demolinera-protocol/linera-web
: consumed locally as@linera/client
via the git submodule
- Node 18+
- pnpm (recommended)
- Optional (only if rebuilding Linera Web locally): Rust toolchain and whatever
linera-protocol/linera-web
requires
git clone <this-repo>
cd linera-vites
git submodule init
git submodule update --remote
The app depends on linera-protocol
via a git submodule at linera-protocol/
and references linera-web
as a local dependency.
Create a .env.local
in the project root with:
VITE_DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id
Note: Vite only exposes variables prefixed with VITE_
to the client.
pnpm install
pnpm dev
Open http://localhost:5173
.
To build and preview a production bundle:
pnpm build
pnpm preview
- Click "Connect Wallet" to authenticate with Dynamic and connect your wallet.
- After wallet connection, click "Connect to Linera" to create a chain and connect to the Linera network.
- Click "Connect to App" to set the counter application on your chain.
- Use "Get Count" to read the current counter value and "Increment Count" to increase it by 1.
- The "Blocks" panel will update automatically when new blocks arrive on your chain.
You can update LINERA_RPC_URL
and COUNTER_APP_ID
in src/constants.ts
to point at a different faucet/application.
- Linera Web is consumed from the local submodule (
linera-protocol/linera-web
) via@linera/client
. - The Vite dev server sends COOP/COEP headers, making the page cross-origin isolated, which enables SharedArrayBuffer and worker isolation.
- Dynamic is initialized in
src/main.tsx
viaDynamicContextProvider
usingVITE_DYNAMIC_ENVIRONMENT_ID
. - The signer bridge in
src/lib/dynamic-signer.ts
implements Linera’sSigner
by delegating to the connected wallet (EIP-191personal_sign
). - The small
dynamic-iframe-shim.js
is loaded fromindex.html
before the app to mark Dynamic-hosted iframes as credentialless when COEP/COOP are enabled.
- SharedArrayBuffer or worker errors: Ensure you're running through
pnpm dev
so the COOP/COEP headers invite.config.ts
are applied. These headers are required for Linera Web's cross-origin isolation. - @linera/client assets fail to load: After updating the submodule, re-run
pnpm install
to refresh the local file dependency. - Build errors: Make sure Node.js 18+ is installed and you're using pnpm as the package manager.
- Dynamic login iframe blocked or not loading: Ensure
index.html
includespublic/js/dynamic-iframe-shim.js
before your app script. The shim setscredentialless
on Dynamic-hosted iframes (e.g.,relay.dynamicauth.com
). If using a custom Dynamic domain, update the host-matching logic in the shim. Verify in DevTools that the login iframe hascredentialless
set and no "blocked by response" errors. - Missing VITE_DYNAMIC_ENVIRONMENT_ID: Create a
.env.local
file in the project root with your Dynamic environment ID.
- "Failed to connect to Linera" error: Check that
LINERA_RPC_URL
insrc/constants.ts
is reachable:curl -I <LINERA_RPC_URL>
should return HTTP 200. The faucet may be down or the RPC URL may be incorrect. - "Failed to get application" or similar errors: After clicking "Connect to App", if you see application errors, ensure
COUNTER_APP_ID
exists on the same network asLINERA_RPC_URL
. - Connection succeeds but no address/chain ID shown: Verify the faucet is operational and the RPC URL is correct.
- Mismatched environments: App IDs from different networks than the faucet will prevent queries from succeeding. Cross-check official testnet values at Linera demos testnet .env.
- "Connect to App" button not appearing: Ensure you've successfully connected to Linera first by checking for your address and chain ID in the results panel.
- Counter not updating: Make sure you've clicked "Connect to App" before trying to get or increment the count.
- Block notifications not appearing: Blocks only appear after successful application interactions. The subscription is established after connecting to the app.
- Slow loading: The Linera Web WASM bundle is large; first load may take time.
- Memory usage: Linera Web runs WASM in workers; monitor browser memory if experiencing issues.