mcpField
D
dynamic-midnight-wallet

dynamic-midnight-wallet

github.com/kali-decoder開発18125 回インストール1 日前に更新
リポジトリ

dynamic-midnight-wallet について

Dynamic.xyz経由でMidnightウォレットに接続するReact、Vite、TypeScriptアプリを生成するスキルです。1AM拡張機能と組み込みウォレットの両方を扱い、型付きのアドレス、残高、送金アクセスを提供します。

主な機能

  1. 01React + Vite + TypeScriptアプリのひな形
  2. 02Dynamicウォレット接続の設定
  3. 031AMと組み込みウォレットのフロー
  4. 04型付きMidnightアドレスと残高へのアクセス
  5. 05Shieldedとunshieldedのトークン送金

活用シーン

  1. 011AMウォレットに接続するWebアプリの構築
  2. 02ソーシャルまたはメール対応の組み込みMidnightウォレット追加
  3. 03Shielded、unshielded、DUSTの状態表示
  4. 04NIGHT送金を対応するプールへ振り分け

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/dynamic-midnight-walletを入力して読み込まれたか確認します。

詳細

Dynamic Midnight Wallet スキル

このスキルでは、Dynamic.xyz(@dynamic-labs/midnight)を通じてMidnightウォレットに接続する、完全なReact + Vite + TypeScriptアプリを生成します。手作業でのwindow.midnight接続処理を、DynamicContextProvider、DynamicWidget、型付きの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/[email protected] + @dynamic-labs/[email protected](同じバージョンに固定)

主要なアーキテクチャ上の注意点:

  • Midnightウォレットは単一のアドレスではなく、3つの領域があります。
    領域役割Dynamic API
    Unshielded公開アドレス/状態wallet.address
    Shieldedプライベートトークンプールwallet.additionalAddresses(MidnightShielded)
    DUST手数料生成状態wallet.additionalAddresses(MidnightDust)
  • NIGHTはShieldedとUnshieldedの両方に存在します。プールが異なるため、残高が重複しているわけではありません。
  • 接続ライフサイクル、コネクター、アドレス/残高アクセサー、送金ルーティングはDynamicが管理します。
  • キーの保管、証明の生成、署名、送信は、注入型の1AM拡張機能が管理します。Dynamicは@midnight-ntwrk/dapp-connector-apiを介して接続します。
  • 入金UXはアプリが管理します。UnshieldedとShieldedの両方のアドレスを表示してください。DUSTは生成されるもので、入金先ではありません。
  • sendBalanceは受取人のプレフィックスに応じてルーティングします。mn_shield...の場合はShieldedプール、それ以外の場合はUnshieldedプールです。プール間の送金はできません。
  • Midnightのメソッドを呼び出す前に、必ずisMidnightWallet(wallet)で判定してください。

他のスキルとの関係:

  • Dynamicを使わない生のDApp Connector → react-wallet-connector/
  • 1AMを直接使ったコントラクトのデプロイと回路呼び出し → 1am-wallet/
  • 埋め込みウォレットのみ → このスキルの§17(DynamicWaasMidnightConnectors)を参照

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

2) 前提条件

  • Node.js 18+
  • Chains & NetworksでMidnightを有効化したDynamic.xyzアカウント
  • ダッシュボードから取得したDynamicのEnvironment ID
  • 注入型のフローの場合:1AMウォレットのブラウザー拡張機能をインストール
  • 埋め込み型のフローの場合:Embedded Wallets → SecurityでPrivate Key Exportsを有効化(MPC Midnightウォレットに必要)

3) スキャフォールドとインストール

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

.envを作成します。

VITE_DYNAMIC_ENVIRONMENT_ID=your-environment-id-here

4) package.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

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

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

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

標準のVite react-tsテンプレートの設定を使用します(react-wallet-connector/SKILL.md §6–8と同じです)。


7) src/vite-env.d.ts

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

interface ImportMetaEnv {
  readonly VITE_DYNAMIC_ENVIRONMENT_ID: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

8) 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>,
);

9) src/App.tsx

注入型の1AM拡張機能フロー(デフォルトのテンプレート):

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

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

3つの領域と残高をすべて表示します。CSSは含まれていません。Tailwindまたはお好みのスタイルを追加してください。

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>
  );
}

12) 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>

13) .env.example

VITE_DYNAMIC_ENVIRONMENT_ID=

14) 実行

cp .env.example .env
# Paste your Dynamic Environment ID
npm install
npm run dev

http://localhost:5173を開きます。DynamicWidgetをクリックして1AMウォレットに接続すると、パネルに3つの領域と残高が表示されます。


15) ウォレットの種類を確認する

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

if (!isMidnightWallet(wallet)) {
  throw new Error('This wallet is not a Midnight wallet');
}

getFormattedBalances()、sendBalance()、またはコネクターのメソッドを呼び出す前に、必ず型を絞り込んでください。


16) より詳細なShieldedハンドル(Connector API)

回路でコイン/暗号化の公開鍵が必要な場合:

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) 埋め込みウォレット(ソーシャル/メール)

App.tsx でコネクターを切り替えます。

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

<DynamicContextProvider
  settings={{
    environmentId,
    walletConnectors: [DynamicWaasMidnightConnectors],
  }}
>

ダッシュボードの要件:

  1. Chains & Networks で Midnight を有効にします
  2. Embedded Wallets → Security で Private Key Exports を有効にします(無効の場合、すべての操作で 403 が発生します)

埋め込みウォレットの残高の形式は少し異なります。

const { unshieldedBalance, shieldedTokenCount, dustBalance, dustSyncing } =
  await primaryWallet.getFormattedBalances();

// Poll until DUST sync completes (WaaS only)
if (dustSyncing) {
  setTimeout(() => void refreshBalances(), 2500);
}

埋め込みウォレット専用の追加機能:registerDust()、signMessage()、および createTransferTransaction → signTransaction → submitTransaction の順に実行するステップごとの処理。


18) トークンの送信

// 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();

19) 責任の境界

レイヤー担当
Dynamic接続のライフサイクル、コネクター、MidnightWallet オブジェクト、アドレス/残高のアクセサー、送信のルーティング
1AM extension鍵の管理、導出、ZK 証明、署名、送信
お使いの DApp入金/受取の UX(未シールドとシールド済みの両方を表示)、表示するプール、DUST 生成に関するメッセージ

20) トラブルシューティング

症状原因対処方法
ウィジェットに Midnight が表示されないチェーンが無効Dynamic ダッシュボード → Chains & Networks で Midnight を有効にします
(埋め込みウォレットで)すべてのウォレットメソッドが 403 になるPrivate Key Exports が無効Embedded Wallets → Security で有効にします
isMidnightWallet が falseコネクターまたはチェーンが間違っているMidnightWalletConnectors または DynamicWaasMidnightConnectors を使用します
シールド済みアドレスが表示されないウォレットがまだ同期中接続後に再試行します。WaaS の場合は dustSyncing をポーリングします
プールをまたぐ送信に失敗するmn_addr → mn_shield同じプールのみです。未シールドから未シールドへ、シールド済みからシールド済みへ送信します
バージョンの不一致エラー@dynamic-labs/* のバージョンが混在しているすべての Dynamic パッケージを同じバージョンに固定します
DUST が空NIGHT が登録されていない未シールドの NIGHT に資金を入金した後、registerDust() を呼び出します(埋め込みウォレットの場合)

21) エージェント用チェックリスト

ユーザー向けにこのテンプレートを生成する場合:

  • 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) 関連スキル

必要なものスキル
Dynamic を使わない DApp コネクターreact-wallet-connector/
コントラクトのデプロイと回路1am-wallet/
トークン単位とプールtoken-transfers/
Locker/ペイメントボールト DAppexample-locker-dapp/、example-payment-dapp/

コメント