dynamic-midnight-wallet

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Dynamic Midnight Wallet Skill

Dynamic Midnight钱包技能

This skill generates a complete React + Vite + TypeScript app that connects Midnight wallets through Dynamic.xyz (
@dynamic-labs/midnight
). It replaces hand-rolled
window.midnight
wiring with
DynamicContextProvider
,
DynamicWidget
, and typed Midnight wallet accessors.
Primary references:
  • dynamic.xyz/docs/react/wallets/using-wallets/midnight/using-midnight-wallets
    — injected 1AM extension
  • dynamic.xyz/docs/react/wallets/using-wallets/midnight/midnight-embedded-wallets
    — social/email MPC wallets
  • dynamic.xyz/docs/llms.txt
    — full Dynamic documentation index
  • @dynamic-labs/midnight@4.91.2
    +
    @dynamic-labs/sdk-react-core@4.91.2
    (pin together)
Key architecture notes:
  • A Midnight wallet is not a single address — it has three surfaces:
    SurfaceRoleDynamic API
    UnshieldedPublic address/state
    wallet.address
    ShieldedPrivate token pool
    wallet.additionalAddresses
    (
    MidnightShielded
    )
    DUSTFee-generation state
    wallet.additionalAddresses
    (
    MidnightDust
    )
  • NIGHT
    exists as both shielded and unshielded — different pools, not duplicate balances
  • Dynamic owns connection lifecycle, connectors, address/balance accessors, send routing
  • 1AM extension (injected) owns key custody, proving, signing, submission — Dynamic talks to it via
    @midnight-ntwrk/dapp-connector-api
  • Your app owns deposit UX — surface both unshielded and shielded addresses; DUST is generated, not deposited to
  • sendBalance
    routes by recipient prefix:
    mn_shield...
    → shielded pool, otherwise unshielded. No cross-pool transfers
  • Always guard with
    isMidnightWallet(wallet)
    before calling Midnight methods
Relationship to other skills:
  • Raw DApp Connector without Dynamic →
    react-wallet-connector/
  • Contract deploy + circuit calls via 1AM directly →
    1am-wallet/
  • Embedded wallets only → see §17 in this skill (
    DynamicWaasMidnightConnectors
    )

本技能可生成完整的React + Vite + TypeScript应用,通过Dynamic.xyz
@dynamic-labs/midnight
)连接Midnight钱包。它使用
DynamicContextProvider
DynamicWidget
和类型化Midnight钱包访问器替代手动编写的
window.midnight
连接逻辑。
主要参考资料:
  • dynamic.xyz/docs/react/wallets/using-wallets/midnight/using-midnight-wallets
    — 注入式1AM扩展
  • dynamic.xyz/docs/react/wallets/using-wallets/midnight/midnight-embedded-wallets
    — 社交/邮箱MPC钱包
  • dynamic.xyz/docs/llms.txt
    — Dynamic完整文档索引
  • @dynamic-labs/midnight@4.91.2
    +
    @dynamic-labs/sdk-react-core@4.91.2
    (需锁定同一版本)
核心架构说明:
  • Midnight钱包并非单一地址——它拥有三种交互层面
    层面作用Dynamic API
    Unshielded(未屏蔽)公开地址/状态
    wallet.address
    Shielded(已屏蔽)私有代币池
    wallet.additionalAddresses
    MidnightShielded
    类型)
    DUST手续费生成状态
    wallet.additionalAddresses
    MidnightDust
    类型)
  • NIGHT
    代币同时存在于已屏蔽和未屏蔽池中——是不同的资金池,而非重复余额
  • Dynamic负责连接生命周期、连接器、地址/余额访问器、转账路由
  • 1AM扩展(注入式)负责密钥托管、零知识证明、签名、交易提交——Dynamic通过
    @midnight-ntwrk/dapp-connector-api
    与其通信
  • 你的应用负责存款用户体验——需展示未屏蔽和已屏蔽两种地址;DUST由系统生成,无需直接存款
  • sendBalance
    根据接收方前缀路由:
    mn_shield...
    → 已屏蔽池,其他则为未屏蔽池。不支持跨池转账
  • 调用Midnight方法前,务必使用
    isMidnightWallet(wallet)
    进行校验
与其他技能的关联:
  • 不使用Dynamic的原生DApp连接器 →
    react-wallet-connector/
  • 通过1AM直接部署合约+调用电路 →
    1am-wallet/
  • 仅使用嵌入式钱包 → 参考本技能第17节(
    DynamicWaasMidnightConnectors

1) Project Structure

1) 项目结构

my-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.ts

my-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.ts

2) Prerequisites

2) 前置条件

  • Node.js 18+
  • Dynamic.xyz account with Midnight enabled under Chains & Networks
  • Dynamic Environment ID from the dashboard
  • For injected flow: 1AM wallet browser extension installed
  • For embedded flow: enable Private Key Exports under Embedded Wallets → Security (required for MPC Midnight wallets)

  • Node.js 18+
  • Dynamic.xyz账户,且已在「Chains & Networks」中启用Midnight
  • 从控制台获取的Dynamic Environment ID
  • 注入式流程:已安装1AM wallet浏览器扩展
  • 嵌入式流程:在「Embedded Wallets → Security」中启用Private Key Exports(MPC Midnight钱包必需)

3) Scaffold & Install

3) 搭建与安装

bash
npm 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
Create
.env
:
bash
VITE_DYNAMIC_ENVIRONMENT_ID=your-environment-id-here

bash
npm 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
创建
.env
文件:
bash
VITE_DYNAMIC_ENVIRONMENT_ID=your-environment-id-here

4)
package.json

4)
package.json

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 all
@dynamic-labs/*
packages to the same version. Check
npm view @dynamic-labs/midnight version
if install fails.

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"
  }
}
所有
@dynamic-labs/*
包需锁定同一版本。若安装失败,可通过
npm view @dynamic-labs/midnight version
查看最新版本。

5)
vite.config.ts

5)
vite.config.ts

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

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

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

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

6)
tsconfig.json
/
tsconfig.app.json
/
tsconfig.node.json

6)
tsconfig.json
/
tsconfig.app.json
/
tsconfig.node.json

Use the standard Vite
react-ts
template configs (same as
react-wallet-connector/SKILL.md
§6–8).

使用标准Vite
react-ts
模板配置(与
react-wallet-connector/SKILL.md
第6–8节一致)。

7)
src/vite-env.d.ts

7)
src/vite-env.d.ts

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

interface ImportMetaEnv {
  readonly VITE_DYNAMIC_ENVIRONMENT_ID: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

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

interface ImportMetaEnv {
  readonly VITE_DYNAMIC_ENVIRONMENT_ID: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

8)
src/main.tsx

8)
src/main.tsx

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

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

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

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

9)
src/App.tsx

9)
src/App.tsx

Injected 1AM extension flow (default template):
tsx
import { 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>
  );
}

注入式1AM扩展流程(默认模板):
tsx
import { 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>
  );
}

10)
src/hooks/useMidnightWallet.ts

10)
src/hooks/useMidnightWallet.ts

typescript
import { 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,
  };
}

typescript
import { 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,
  };
}

11)
src/MidnightWalletPanel.tsx

11)
src/MidnightWalletPanel.tsx

Displays all three surfaces and balances. No CSS — add Tailwind or your preferred styling.
tsx
import { 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>
  );
}

展示所有三种交互层面及余额。未包含CSS——可自行添加Tailwind或其他样式方案。
tsx
import { 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>通过上方组件连接你的Midnight钱包。</p>;
  }

  return (
    <div>
      <h2>钱包交互层面</h2>
      <dl>
        <dt>未屏蔽(公开)</dt>
        <dd title={unshieldedAddress}>{unshieldedAddress}</dd>
        <dt>已屏蔽(私有)</dt>
        <dd title={shieldedAddress ?? ''}>{shieldedAddress ?? '—'}</dd>
        <dt>DUST(手续费)</dt>
        <dd title={dustAddress ?? ''}>{dustAddress ?? '—'}</dd>
      </dl>

      <h2>余额</h2>
      {loading ? <p>加载中…</p> : null}
      {error ? <p role="alert">{error}</p> : null}
      {balances ? (
        <ul>
          <li>未屏蔽NIGHT: {balances.unshieldedBalance ?? '0'}</li>
          <li>已屏蔽NIGHT: {balances.shieldedBalance ?? '0'}</li>
          <li>
            DUST:{' '}
            {balances.dustBalance
              ? `${balances.dustBalance.balance} / 上限 ${balances.dustBalance.cap}`
              : '—'}
          </li>
        </ul>
      ) : null}
      <button type="button" onClick={() => void refreshBalances()}>
        刷新余额
      </button>

      <h2>存款说明</h2>
      <p>
        发送至<strong>未屏蔽</strong>地址以存入公开NIGHT,发送至<strong>已屏蔽</strong>地址以存入私有NIGHT。DUST由已注册的未屏蔽NIGHT生成——无需直接向其存款。
      </p>
    </div>
  );
}

12)
index.html

12)
index.html

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>

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>

13)
.env.example

13)
.env.example

bash
VITE_DYNAMIC_ENVIRONMENT_ID=

bash
VITE_DYNAMIC_ENVIRONMENT_ID=

14) Run

14) 运行

bash
cp .env.example .env
bash
cp .env.example .env

Paste your Dynamic Environment ID

粘贴你的Dynamic Environment ID

npm install npm run dev

Open `http://localhost:5173`. Click **DynamicWidget** → connect 1AM wallet → panel shows three surfaces and balances.

---
npm install npm run dev

打开`http://localhost:5173`。点击**DynamicWidget** → 连接1AM钱包 → 面板将展示三种交互层面及余额。

---

15) Checking Wallet Type

15) 校验钱包类型

tsx
import { isMidnightWallet } from '@dynamic-labs/midnight';

if (!isMidnightWallet(wallet)) {
  throw new Error('This wallet is not a Midnight wallet');
}
Always narrow before calling
getFormattedBalances()
,
sendBalance()
, or connector methods.

tsx
import { isMidnightWallet } from '@dynamic-labs/midnight';

if (!isMidnightWallet(wallet)) {
  throw new Error('该钱包并非Midnight钱包');
}
调用
getFormattedBalances()
sendBalance()
或连接器方法前,务必先进行类型校验。

16) Richer Shielded Handles (Connector API)

16) 增强已屏蔽地址处理(连接器API)

When circuits need coin/encryption public keys:
tsx
const connector = primaryWallet.connector;

const { shieldedAddress, shieldedCoinPublicKey, shieldedEncryptionPublicKey } =
  await connector.getShieldedAddresses();
const { unshieldedAddress } = await connector.getUnshieldedAddress();
const { dustAddress } = await connector.getDustAddress();
Use these when wiring
1am-wallet/
provider sessions on top of Dynamic.

当电路需要代币/加密公钥时:
tsx
const connector = primaryWallet.connector;

const { shieldedAddress, shieldedCoinPublicKey, shieldedEncryptionPublicKey } =
  await connector.getShieldedAddresses();
const { unshieldedAddress } = await connector.getUnshieldedAddress();
const { dustAddress } = await connector.getDustAddress();
在Dynamic之上集成
1am-wallet/
提供方会话时可使用这些方法。

17) Embedded Wallets (Social / Email)

17) 嵌入式钱包(社交/邮箱)

Swap connector in
App.tsx
:
tsx
import { DynamicWaasMidnightConnectors } from '@dynamic-labs/midnight';

<DynamicContextProvider
  settings={{
    environmentId,
    walletConnectors: [DynamicWaasMidnightConnectors],
  }}
>
Dashboard requirements:
  1. Enable Midnight under Chains & Networks
  2. Enable Private Key Exports under Embedded Wallets → Security (403 on all ops if disabled)
Embedded balance shape differs slightly:
tsx
const { unshieldedBalance, shieldedTokenCount, dustBalance, dustSyncing } =
  await primaryWallet.getFormattedBalances();

// Poll until DUST sync completes (WaaS only)
if (dustSyncing) {
  setTimeout(() => void refreshBalances(), 2500);
}
Embedded-only extras:
registerDust()
,
signMessage()
, step-by-step
createTransferTransaction
signTransaction
submitTransaction
.

App.tsx
中替换连接器:
tsx
import { DynamicWaasMidnightConnectors } from '@dynamic-labs/midnight';

<DynamicContextProvider
  settings={{
    environmentId,
    walletConnectors: [DynamicWaasMidnightConnectors],
  }}
>
控制台要求:
  1. 在「Chains & Networks」中启用Midnight
  2. 在「Embedded Wallets → Security」中启用Private Key Exports(若禁用,所有操作将返回403错误)
嵌入式钱包的余额结构略有不同:
tsx
const { unshieldedBalance, shieldedTokenCount, dustBalance, dustSyncing } =
  await primaryWallet.getFormattedBalances();

// 轮询直至DUST同步完成(仅WaaS)
if (dustSyncing) {
  setTimeout(() => void refreshBalances(), 2500);
}
嵌入式钱包专属功能:
registerDust()
signMessage()
、分步式
createTransferTransaction
signTransaction
submitTransaction

18) Sending Tokens

18) 发送代币

tsx
// Routes by recipient prefix — mn_shield... = shielded, else unshielded
await primaryWallet.sendBalance({
  toAddress: 'mn_shield...',
  amount: '1.5', // human-readable NIGHT for extension wallets
});
Cross-pool transfers are not supported. Sender and recipient must be in the same pool.
For raw per-token amounts:
tsx
const { shielded, unshielded, dust } = await primaryWallet.getBalances();

tsx
// 根据接收方前缀路由——mn_shield... = 已屏蔽池,其他为未屏蔽池
await primaryWallet.sendBalance({
  toAddress: 'mn_shield...',
  amount: '1.5', // 扩展钱包使用人类可读格式的NIGHT
});
不支持跨池转账。发送方与接收方必须处于同一资金池。
如需获取原始代币余额:
tsx
const { shielded, unshielded, dust } = await primaryWallet.getBalances();

19) Ownership Boundaries

19) 职责边界

LayerOwns
DynamicConnection lifecycle, connectors,
MidnightWallet
object, address/balance accessors, send routing
1AM extensionKey custody, derivation, ZK proving, signing, submission
Your DAppDeposit/receive UX (show unshielded and shielded), which pool to display, DUST generation messaging

层级负责内容
Dynamic连接生命周期、连接器、
MidnightWallet
对象、地址/余额访问器、转账路由
1AM扩展密钥托管、派生、零知识证明、签名、交易提交
你的DApp存款/收款用户体验(展示未屏蔽已屏蔽地址)、显示哪个资金池、DUST生成相关提示

20) Troubleshooting

20) 故障排查

SymptomCauseFix
Midnight not in widgetChain disabledEnable Midnight in Dynamic dashboard → Chains & Networks
403 on all wallet methods (embedded)Private Key Exports offEnable under Embedded Wallets → Security
isMidnightWallet
false
Wrong connector or chainUse
MidnightWalletConnectors
or
DynamicWaasMidnightConnectors
Shielded address missingWallet still syncingRetry after connect; poll
dustSyncing
for WaaS
Send fails cross-pool
mn_addr
mn_shield
Same pool only — unshielded to unshielded, shielded to shielded
Version mismatch errorsMixed
@dynamic-labs/*
versions
Pin all Dynamic packages to same version
Empty DUSTNIGHT not registeredCall
registerDust()
after funding unshielded NIGHT (embedded)

症状原因解决方法
组件中未显示Midnight链未启用在Dynamic控制台「Chains & Networks」中启用Midnight
所有钱包方法返回403(嵌入式)未开启私钥导出在「Embedded Wallets → Security」中启用该功能
isMidnightWallet
返回false
连接器或链错误使用
MidnightWalletConnectors
DynamicWaasMidnightConnectors
已屏蔽地址缺失钱包仍在同步连接后重试;WaaS需轮询
dustSyncing
状态
跨池转账失败
mn_addr
mn_shield
仅支持同池转账——未屏蔽转未屏蔽,已屏蔽转已屏蔽
版本不匹配错误
@dynamic-labs/*
版本不一致
所有Dynamic包锁定同一版本
DUST余额为空NIGHT未注册存入未屏蔽NIGHT后调用
registerDust()
(嵌入式)

21) Agent Checklist

21) Agent检查清单

When generating this template for a user:
  • Scaffold Vite
    react-ts
    or write all files from this skill
  • Install
    @dynamic-labs/sdk-react-core
    ,
    @dynamic-labs/midnight
    ,
    @dynamic-labs/sdk-api-core
    at same version
  • Set
    VITE_DYNAMIC_ENVIRONMENT_ID
    in
    .env
  • Use
    MidnightWalletConnectors
    for extension (default) or
    DynamicWaasMidnightConnectors
    for embedded
  • Include
    DynamicWidget
    for connect/disconnect
  • Surface unshielded, shielded, and DUST in deposit UI
  • Guard all Midnight calls with
    isMidnightWallet
  • Omit CSS unless user requests styling
  • Link to
    1am-wallet/
    if user needs contract deploy on top of Dynamic connection

为用户生成此模板时需确保:
  • 搭建Vite
    react-ts
    项目或根据本技能编写所有文件
  • 安装同一版本的
    @dynamic-labs/sdk-react-core
    @dynamic-labs/midnight
    @dynamic-labs/sdk-api-core
  • .env
    中设置
    VITE_DYNAMIC_ENVIRONMENT_ID
  • 扩展钱包使用
    MidnightWalletConnectors
    (默认),嵌入式钱包使用
    DynamicWaasMidnightConnectors
  • 包含
    DynamicWidget
    用于连接/断开钱包
  • 在存款UI中展示未屏蔽已屏蔽DUST地址
  • 所有Midnight调用前使用
    isMidnightWallet
    校验
  • 除非用户要求,否则不包含CSS
  • 若用户需要在Dynamic连接之上部署合约,链接至
    1am-wallet/
    技能

22) Related Skills

22) 相关技能

NeedSkill
DApp Connector without Dynamic
react-wallet-connector/
Contract deploy + circuits
1am-wallet/
Token units & pools
token-transfers/
Locker / payment vault dApps
example-locker-dapp/
,
example-payment-dapp/
需求技能
不使用Dynamic的DApp连接器
react-wallet-connector/
合约部署+电路调用
1am-wallet/
代币单位与资金池
token-transfers/
锁仓/支付金库DApp
example-locker-dapp/
,
example-payment-dapp/