mcpField
R
react-wallet-connector

react-wallet-connector

github.com/kali-decoderDevelopment18127 installsUpdated 1d ago
Repository

About react-wallet-connector

A skill that generates a runnable React and TypeScript app for connecting to a Midnight wallet extension through the DApp Connector API. It provides a frontend connection foundation with wallet discovery, authorization, address display, and optional wallet selection.

Key features

  1. 01Vite React TypeScript app scaffold
  2. 02Discover injected Midnight wallets
  3. 03Connect through the DApp Connector API
  4. 04Show connection status and unshielded address
  5. 05Optional multi-wallet picker
  6. 06Network selection guidance

Use cases

  1. 01Create a wallet-connected React app
  2. 02Let users select among installed wallets
  3. 03Request wallet authorization for a network
  4. 04Display wallet connection and address details

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/kali-decoder/midnight-skills --skill react-wallet-connector

After installing, type/react-wallet-connectorin Claude Code to confirm it loaded.

Details

From the project README

React Wallet Connector Skill

This skill generates a complete, runnable React application that connects to a Midnight wallet extension using the DApp Connector API. It covers every file needed: Vite + React + TypeScript scaffold, wallet selection, connection logic, and a WalletCard UI component.

Primary references:

  • docs.midnight.network — React wallet connector guide (DApp Connector API)
  • llms.txt — full Midnight documentation index
  • @midnight-ntwrk/[email protected] — latest stable connector package (Feb 2026)

Key architecture notes:

  • Wallets inject InitialAPI instances on window.midnight, each keyed by a UUID — never use hardcoded keys like window.midnight.mnLace
  • Enumerate wallets with Object.values(window.midnight); when multiple wallets exist, let the user choose
  • Import @midnight-ntwrk/dapp-connector-api as a side effect to augment global window.midnight types
  • Request the shielded address only when your app actually needs it; the template uses getUnshieldedAddress()
  • Network IDs: 'undeployed' (local), 'preview', 'preprod', 'mainnet' — must match the wallet's configured network
  • Code examples omit CSS styling by design; add your preferred styling solution after scaffolding

Relationship to other skills:

  • For full contract deploy + circuit calls via 1AM wallet → use 1am-wallet/
  • For headless Node.js wallet + tests → use example-hello-world/ or midnight-js/
  • This skill is the frontend wallet connection foundation only

1) Project Structure

my-wallet-app/
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.node.json
├── vite.config.ts
└── src/
    ├── main.tsx
    ├── App.tsx
    ├── WalletCard.tsx
    ├── types.ts
    ├── selectWallet.ts
    └── vite-env.d.ts

2) Prerequisites

  • Node.js 18+ and npm
  • Basic React + TypeScript familiarity
  • A Midnight wallet browser extension installed (1AM, Lace, or any DApp Connector–compatible wallet)

3) Scaffold & Install

npm create vite@latest my-wallet-app -- --template react-ts
cd my-wallet-app
npm install @midnight-ntwrk/[email protected]

4) package.json

Use the Vite react-ts template output and ensure @midnight-ntwrk/dapp-connector-api is listed:

{
  "name": "my-wallet-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@midnight-ntwrk/dapp-connector-api": "4.0.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "@vitejs/plugin-react": "^4.3.0",
    "typescript": "~5.7.0",
    "vite": "^6.0.0"
  }
}

Pin @midnight-ntwrk/dapp-connector-api to 4.0.1 unless the user specifies otherwise. Check npm view @midnight-ntwrk/dapp-connector-api version if install fails with ETARGET.


5) vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
});

6) tsconfig.json

{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}

7) tsconfig.app.json

{
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true
  },
  "include": ["src"]
}

8) tsconfig.node.json

{
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
    "target": "ES2022",
    "lib": ["ES2023"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true
  },
  "include": ["vite.config.ts"]
}

9) src/vite-env.d.ts

/// <reference types="vite/client" />

The @midnight-ntwrk/dapp-connector-api side-effect import in App.tsx augments window.midnight — no manual declaration needed.


10) src/types.ts

export interface WalletCardProps {
  isConnected: boolean;
  walletAddress: string | null;
  onConnect: () => void;
  onDisconnect: () => void;
}

11) src/selectWallet.ts

Wallets inject under window.midnight with UUID keys. Always enumerate — never hardcode a wallet name.

import type { InitialAPI } from '@midnight-ntwrk/dapp-connector-api';

export const listWallets = (): InitialAPI[] => {
  const injected = window.midnight;
  return injected ? Object.values(injected) : [];
};

export const selectWallet = (): InitialAPI => {
  const wallets = listWallets();

  if (wallets.length === 0) {
    throw new Error(
      'No Midnight wallet found. Please install a Midnight wallet extension.',
    );
  }

  return wallets[0];
};

When more than one wallet is available, render a picker using listWallets() and let the user choose. Display each wallet's name and icon safely to prevent XSS.


12) src/WalletCard.tsx

Presentation layer — connection status, address display, connect/disconnect buttons. No CSS included; add styling as needed.

import React from 'react';
import type { WalletCardProps } from './types';

const WalletCard: React.FC<WalletCardProps> = ({
  isConnected,
  walletAddress,
  onConnect,
  onDisconnect,
}) => {
  return (
    <div>
      <div>
        <h2>Connection Status</h2>
        <div>{isConnected ? 'Connected' : 'Disconnected'}</div>
      </div>

      <div>
        {isConnected && walletAddress ? (
          <>
            <p>Wallet Address:</p>
            <p title={walletAddress}>{walletAddress}</p>
          </>
        ) : (
          <p>Please connect your wallet to proceed.</p>
        )}
      </div>

      <div>
        {isConnected ? (
          <button onClick={onDisconnect}>Disconnect Wallet</button>
        ) : (
          <button onClick={onConnect}>Connect Wallet</button>
        )}
      </div>
    </div>
  );
};

export default WalletCard;

13) src/App.tsx

Connection logic using the DApp Connector API.

import React, { useState } from 'react';
import WalletCard from './WalletCard';
import '@midnight-ntwrk/dapp-connector-api';
import { selectWallet } from './selectWallet';

const App: React.FC = () => {
  const [isConnected, setIsConnected] = useState<boolean>(false);
  const [walletAddress, setWalletAddress] = useState<string | null>(null);

  const handleConnect = async () => {
    console.log('Connect button clicked');
    let connected = false;
    let address: string | null = null;

    try {
      const wallet = selectWallet();

      // 'undeployed' for local dev; 'preprod' | 'preview' | 'mainnet' for live networks
      const connectedApi = await wallet.connect('preprod');

      const { unshieldedAddress } = await connectedApi.getUnshieldedAddress();
      address = unshieldedAddress;

      const serviceUriConfig = await connectedApi.getConfiguration();
      console.log('Service URI Config:', serviceUriConfig);

      const connectionStatus = await connectedApi.getConnectionStatus();
      if (connectionStatus.status === 'connected') {
        connected = true;
        console.log('Connected to the wallet:', address);
      }
    } catch (error) {
      console.log('An error occurred:', error);
    }

    setIsConnected(connected);
    setWalletAddress(address);
  };

  const handleDisconnect = () => {
    setWalletAddress(null);
    setIsConnected(false);
  };

  return (
    <div>
      <header>
        <h1>Midnight Wallet Connector</h1>
      </header>
      <main>
        <WalletCard
          isConnected={isConnected}
          walletAddress={walletAddress}
          onConnect={handleConnect}
          onDisconnect={handleDisconnect}
        />
      </main>
    </div>
  );
};

export default App;
Connection flow breakdown
  1. Select wallet — selectWallet() reads window.midnight entries; throws if none found (caught by try/catch)
  2. Connect to network — wallet.connect(networkId) prompts the user to authorize
  3. Retrieve address — getUnshieldedAddress() returns the public unshielded address
  4. Check status — getConnectionStatus() resolves to { status: 'connected' | 'disconnected' }
  5. Optional config — getConfiguration() returns service URIs (indexer, node, etc.) for downstream SDK wiring

14) src/main.tsx

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

15) index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Midnight Wallet Connector</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

16) Run

npm install
npm run dev

Open http://localhost:5173. Click Connect Wallet — the extension prompts for authorization. After approval, the app shows connection status and the unshielded address.


17) Multi-Wallet Picker (Optional Enhancement)

When listWallets() returns more than one entry, replace selectWallet() auto-pick with a user-facing selector:

// src/WalletPicker.tsx
import type { InitialAPI } from '@midnight-ntwrk/dapp-connector-api';
import { listWallets } from './selectWallet';

type Props = {
  onSelect: (wallet: InitialAPI) => void;
};

export function WalletPicker({ onSelect }: Props) {
  const wallets = listWallets();

  if (wallets.length === 0) {
    return <p>No Midnight wallet found. Install a wallet extension and refresh.</p>;
  }

  return (
    <ul>
      {wallets.map((wallet) => (
        <li key={wallet.name}>
          <button type="button" onClick={() => onSelect(wallet)}>
            {wallet.name}
          </button>
        </li>
      ))}
    </ul>
  );
}

Render wallet name and icon as text only unless you sanitize HTML. Never use dangerouslySetInnerHTML for wallet-provided icon URLs.


18) Troubleshooting

ErrorCauseFix
window.midnight is undefinedNo extension installed or page loaded before injectionInstall a Midnight wallet; refresh after install
No Midnight wallet foundExtension disabled or wrong detection patternUse Object.values(window.midnight) — not window.midnight.mnLace or other hardcoded keys
Connection rejected / failsNetwork mismatchEnsure connect('preprod') matches the wallet's active network
Connection hangsWallet locked or not syncedUnlock wallet; wait for sync
ETARGET on installStale package versionRun npm view @midnight-ntwrk/dapp-connector-api version and pin the latest
Type errors on window.midnightMissing side-effect importAdd import '@midnight-ntwrk/dapp-connector-api' in App.tsx

19) Next Steps

After a working connector, extend the app:

FeatureSkill / API
Deploy & call Compact contracts1am-wallet/
Token transferstoken-transfers/
Query balances & tx historyindexer/
Sign arbitrary messagesconnectedApi signing methods (see DApp Connector API docs)
Multi-network selectorNetwork dropdown calling wallet.connect(selectedNetwork)
Full SDK provider wiringmidnight-js/

20) Agent Checklist

When generating this template for a user:

  • Scaffold with Vite react-ts or write all files from this skill
  • Install @midnight-ntwrk/[email protected]
  • Create types.ts, selectWallet.ts, WalletCard.tsx, App.tsx, main.tsx
  • Include side-effect import @midnight-ntwrk/dapp-connector-api in App.tsx
  • Use Object.values(window.midnight) for wallet detection — never hardcode wallet keys
  • Default network to 'preprod' unless user specifies local ('undeployed') or other network
  • Omit CSS unless the user requests styling
  • Mention wallet extension must be installed before testing

Comments

Markdown supported