Loading...
Loading...
DISABLED ON PLATFORM — skill file retained for future release. Do not surface in MIDSKILLS index until re-enabled. Scaffold a React + Vite app with Dynamic.xyz Midnight wallet connection via @dynamic-labs/midnight.
npx skill4agent add kali-decoder/midnight-skills dynamic-midnight-wallet@dynamic-labs/midnightwindow.midnightDynamicContextProviderDynamicWidgetdynamic.xyz/docs/react/wallets/using-wallets/midnight/using-midnight-walletsdynamic.xyz/docs/react/wallets/using-wallets/midnight/midnight-embedded-walletsdynamic.xyz/docs/llms.txt@dynamic-labs/midnight@4.91.2@dynamic-labs/sdk-react-core@4.91.2| Surface | Role | Dynamic API |
|---|---|---|
| Unshielded | Public address/state | |
| Shielded | Private token pool | |
| DUST | Fee-generation state | |
NIGHT@midnight-ntwrk/dapp-connector-apisendBalancemn_shield...isMidnightWallet(wallet)react-wallet-connector/1am-wallet/DynamicWaasMidnightConnectorsmy-dynamic-midnight-app/
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.node.json
├── vite.config.ts
├── .env.example
└── src/
├── main.tsx
├── App.tsx
├── MidnightWalletPanel.tsx
├── hooks/
│ └── useMidnightWallet.ts
└── vite-env.d.tsnpm create vite@latest my-dynamic-midnight-app -- --template react-ts
cd my-dynamic-midnight-app
npm install @dynamic-labs/sdk-react-core@4.91.2 @dynamic-labs/midnight@4.91.2 @dynamic-labs/sdk-api-core@4.91.2.envVITE_DYNAMIC_ENVIRONMENT_ID=your-environment-id-herepackage.json{
"name": "my-dynamic-midnight-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"@dynamic-labs/midnight": "4.91.2",
"@dynamic-labs/sdk-api-core": "4.91.2",
"@dynamic-labs/sdk-react-core": "4.91.2",
"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 allpackages to the same version. Check@dynamic-labs/*if install fails.npm view @dynamic-labs/midnight version
vite.config.tsimport { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});tsconfig.jsontsconfig.app.jsontsconfig.node.jsonreact-tsreact-wallet-connector/SKILL.mdsrc/vite-env.d.ts/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_DYNAMIC_ENVIRONMENT_ID: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}src/main.tsximport { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);src/App.tsximport { DynamicContextProvider, DynamicWidget } from '@dynamic-labs/sdk-react-core';
import { MidnightWalletConnectors } from '@dynamic-labs/midnight';
import MidnightWalletPanel from './MidnightWalletPanel';
const environmentId = import.meta.env.VITE_DYNAMIC_ENVIRONMENT_ID;
if (!environmentId) {
throw new Error('Set VITE_DYNAMIC_ENVIRONMENT_ID in .env');
}
export default function App() {
return (
<DynamicContextProvider
settings={{
environmentId,
walletConnectors: [MidnightWalletConnectors],
}}
>
<header>
<h1>Midnight + Dynamic</h1>
<DynamicWidget />
</header>
<main>
<MidnightWalletPanel />
</main>
</DynamicContextProvider>
);
}src/hooks/useMidnightWallet.tsimport { useDynamicContext } from '@dynamic-labs/sdk-react-core';
import { isMidnightWallet } from '@dynamic-labs/midnight';
import { WalletAddressType } from '@dynamic-labs/sdk-api-core';
export function useMidnightWallet() {
const { primaryWallet } = useDynamicContext();
if (!primaryWallet || !isMidnightWallet(primaryWallet)) {
return { wallet: null, isConnected: false as const };
}
const shieldedAddress = primaryWallet.additionalAddresses?.find(
(a) => a.type === WalletAddressType.MidnightShielded,
)?.address;
const dustAddress = primaryWallet.additionalAddresses?.find(
(a) => a.type === WalletAddressType.MidnightDust,
)?.address;
return {
wallet: primaryWallet,
isConnected: true as const,
unshieldedAddress: primaryWallet.address,
shieldedAddress,
dustAddress,
};
}src/MidnightWalletPanel.tsximport { useCallback, useEffect, useState } from 'react';
import { useMidnightWallet } from './hooks/useMidnightWallet';
type Balances = {
shieldedBalance?: string;
unshieldedBalance?: string;
dustBalance?: { balance: string; cap: string };
};
export default function MidnightWalletPanel() {
const { wallet, isConnected, unshieldedAddress, shieldedAddress, dustAddress } =
useMidnightWallet();
const [balances, setBalances] = useState<Balances | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const refreshBalances = useCallback(async () => {
if (!wallet) return;
setLoading(true);
setError(null);
try {
const formatted = await wallet.getFormattedBalances();
setBalances({
shieldedBalance: formatted.shieldedBalance,
unshieldedBalance: formatted.unshieldedBalance,
dustBalance: formatted.dustBalance,
});
} catch (e) {
setError(String(e));
} finally {
setLoading(false);
}
}, [wallet]);
useEffect(() => {
void refreshBalances();
}, [refreshBalances]);
if (!isConnected || !wallet) {
return <p>Connect your Midnight wallet with the widget above.</p>;
}
return (
<div>
<h2>Wallet Surfaces</h2>
<dl>
<dt>Unshielded (public)</dt>
<dd title={unshieldedAddress}>{unshieldedAddress}</dd>
<dt>Shielded (private)</dt>
<dd title={shieldedAddress ?? ''}>{shieldedAddress ?? '—'}</dd>
<dt>DUST (fees)</dt>
<dd title={dustAddress ?? ''}>{dustAddress ?? '—'}</dd>
</dl>
<h2>Balances</h2>
{loading ? <p>Loading…</p> : null}
{error ? <p role="alert">{error}</p> : null}
{balances ? (
<ul>
<li>Unshielded NIGHT: {balances.unshieldedBalance ?? '0'}</li>
<li>Shielded NIGHT: {balances.shieldedBalance ?? '0'}</li>
<li>
DUST:{' '}
{balances.dustBalance
? `${balances.dustBalance.balance} / cap ${balances.dustBalance.cap}`
: '—'}
</li>
</ul>
) : null}
<button type="button" onClick={() => void refreshBalances()}>
Refresh Balances
</button>
<h2>Deposit instructions</h2>
<p>
Send to the <strong>unshielded</strong> address for public NIGHT, or the{' '}
<strong>shielded</strong> address for private NIGHT. DUST is generated from
registered unshielded NIGHT — it is not deposited to directly.
</p>
</div>
);
}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 + Dynamic</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>.env.exampleVITE_DYNAMIC_ENVIRONMENT_ID=cp .env.example .env
# Paste your Dynamic Environment ID
npm install
npm run devhttp://localhost:5173import { isMidnightWallet } from '@dynamic-labs/midnight';
if (!isMidnightWallet(wallet)) {
throw new Error('This wallet is not a Midnight wallet');
}getFormattedBalances()sendBalance()const connector = primaryWallet.connector;
const { shieldedAddress, shieldedCoinPublicKey, shieldedEncryptionPublicKey } =
await connector.getShieldedAddresses();
const { unshieldedAddress } = await connector.getUnshieldedAddress();
const { dustAddress } = await connector.getDustAddress();1am-wallet/App.tsximport { DynamicWaasMidnightConnectors } from '@dynamic-labs/midnight';
<DynamicContextProvider
settings={{
environmentId,
walletConnectors: [DynamicWaasMidnightConnectors],
}}
>const { unshieldedBalance, shieldedTokenCount, dustBalance, dustSyncing } =
await primaryWallet.getFormattedBalances();
// Poll until DUST sync completes (WaaS only)
if (dustSyncing) {
setTimeout(() => void refreshBalances(), 2500);
}registerDust()signMessage()createTransferTransactionsignTransactionsubmitTransaction// Routes by recipient prefix — mn_shield... = shielded, else unshielded
await primaryWallet.sendBalance({
toAddress: 'mn_shield...',
amount: '1.5', // human-readable NIGHT for extension wallets
});const { shielded, unshielded, dust } = await primaryWallet.getBalances();| Layer | Owns |
|---|---|
| Dynamic | Connection lifecycle, connectors, |
| 1AM extension | Key custody, derivation, ZK proving, signing, submission |
| Your DApp | Deposit/receive UX (show unshielded and shielded), which pool to display, DUST generation messaging |
| Symptom | Cause | Fix |
|---|---|---|
| Midnight not in widget | Chain disabled | Enable Midnight in Dynamic dashboard → Chains & Networks |
| 403 on all wallet methods (embedded) | Private Key Exports off | Enable under Embedded Wallets → Security |
| Wrong connector or chain | Use |
| Shielded address missing | Wallet still syncing | Retry after connect; poll |
| Send fails cross-pool | | Same pool only — unshielded to unshielded, shielded to shielded |
| Version mismatch errors | Mixed | Pin all Dynamic packages to same version |
| Empty DUST | NIGHT not registered | Call |
react-ts@dynamic-labs/sdk-react-core@dynamic-labs/midnight@dynamic-labs/sdk-api-coreVITE_DYNAMIC_ENVIRONMENT_ID.envMidnightWalletConnectorsDynamicWaasMidnightConnectorsDynamicWidgetisMidnightWallet1am-wallet/| Need | Skill |
|---|---|
| DApp Connector without Dynamic | |
| Contract deploy + circuits | |
| Token units & pools | |
| Locker / payment vault dApps | |