Skip to content

Dapp Kit

构建 Sui dApp 前端的完整 React 工具包

本节重点

  1. 什么是 dApp Kit?为什么需要它?
  2. 如何安装和配置 dApp Kit?
  3. 如何连接钱包?
  4. 如何查询链上数据?
  5. 如何发送交易?
  6. 如何使用 React Hooks?

什么是 dApp Kit?

@mysten/dapp-kit 是 Sui 官方提供的 React 工具包,专为构建去中心化应用(dApp)前端而设计。它提供了:

  • 🔌 钱包连接:支持多种 Sui 钱包(Sui Wallet、Suiet、Ethos 等)
  • ⚛️ React Hooks:简化链上数据查询和交易发送
  • 🎨 UI 组件:预构建的钱包连接按钮和模态框
  • 🔄 自动同步:实时同步链上状态
  • 💾 状态管理:内置钱包状态和账户管理
  • 📱 响应式设计:支持移动端和桌面端

安装和配置

环境要求

  • React: >= 18.0.0
  • Node.js: >= 16.x
  • typescript: >= 4.5.0(可选但推荐)

安装依赖

bash
# 使用 npm
npm install @mysten/dapp-kit @mysten/sui @tanstack/react-query

# 使用 yarn
yarn add @mysten/dapp-kit @mysten/sui @tanstack/react-query

# 使用 pnpm
pnpm add @mysten/dapp-kit @mysten/sui @tanstack/react-query

基础配置

示例中使用的模块作用如下:

  • SuiClientProvider: 注入网络端点与 Sui 客户端实例,供后续查询与交易 Hook 使用。
  • WalletProvider: 管理钱包列表、连接状态与当前账户,并提供钱包 UI 组件与钱包相关 Hook 的上下文。
  • QueryClient/QueryClientProvider: 通过 React Query 提供异步数据的缓存、请求与状态管理能力。
  • getFullnodeUrl: 快速获取官方 devnet/testnet/mainnet 的 JSON-RPC URL,简化网络配置。
  • @mysten/dapp-kit/dist/index.css: 引入 dApp Kit 组件的基础样式,确保内置 UI 正常显示。
  • networksdefaultNetwork: 定义可用网络映射与默认选择,支持后续动态切换网络。

创建 src/App.tsx

tsx
import { SuiClientProvider, WalletProvider } from '@mysten/dapp-kit';
import { getFullnodeUrl } from '@mysten/sui/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import '@mysten/dapp-kit/dist/index.css';

// 配置
const queryClient = new QueryClient();
const networks = {
  devnet: { url: getFullnodeUrl('devnet') },
  testnet: { url: getFullnodeUrl('testnet') },
  mainnet: { url: getFullnodeUrl('mainnet') },
};

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <SuiClientProvider networks={networks} defaultNetwork="devnet">
        <WalletProvider>
          {/* 你的应用组件 */}
          <YourApp />
        </WalletProvider>
      </SuiClientProvider>
    </QueryClientProvider>
  );
}

export default App;

连接钱包

展示如何在前端连接 Sui 钱包,并管理账户与连接状态,支持内置组件与自定义交互。

使用钱包连接按钮

使用内置的 ConnectButton 快速集成钱包连接流程,适合入门与常见场景。

tsx
import { ConnectButton, useCurrentAccount } from '@mysten/dapp-kit';

function WalletSection() {
  const account = useCurrentAccount();

  return (
    <div>
      <ConnectButton />

      {account && (
        <div>
          <p>已连接地址: {account.address}</p>
        </div>
      )}
    </div>
  );
}

自定义钱包连接

通过 Hooks 手动控制连接、断开与选择钱包,满足自定义 UI 与交互逻辑需求。

tsx
import {
  useConnectWallet,
  useDisconnectWallet,
  useWallets,
  useCurrentAccount,
} from '@mysten/dapp-kit';

function CustomWalletConnect() {
  const wallets = useWallets();
  const { mutate: connect } = useConnectWallet();
  const { mutate: disconnect } = useDisconnectWallet();
  const account = useCurrentAccount();

  if (account) {
    return (
      <div>
        <p>已连接: {account.address}</p>
        <button onClick={() => disconnect()}>
          断开连接
        </button>
      </div>
    );
  }

  return (
    <div>
      <h3>选择钱包</h3>
      {wallets.map((wallet) => (
        <button
          key={wallet.name}
          onClick={() => connect({ wallet })}
          disabled={!wallet.name}
        >
          <img src={wallet.icon} alt={wallet.name} width="32" height="32" />
          {wallet.name}
        </button>
      ))}
    </div>
  );
}

钱包状态管理

读取当前钱包、账户与连接状态,并支持多账户切换,便于动态渲染。

tsx
import {
  useCurrentAccount,
  useCurrentWallet,
  useAccounts,
  useSwitchAccount,
} from '@mysten/dapp-kit';

function WalletInfo() {
  const currentAccount = useCurrentAccount();
  const { currentWallet, connectionStatus } = useCurrentWallet();
  const accounts = useAccounts();
  const { mutate: switchAccount } = useSwitchAccount();

  return (
    <div>
      {/* 连接状态 */}
      <p>状态: {connectionStatus}</p>

      {/* 当前钱包 */}
      {currentWallet && (
        <div>
          <p>钱包: {currentWallet.name}</p>
          <p>版本: {currentWallet.version}</p>
        </div>
      )}

      {/* 当前账户 */}
      {currentAccount && (
        <div>
          <p>地址: {currentAccount.address}</p>
          <p>公钥: {currentAccount.publicKey}</p>
        </div>
      )}

      {/* 切换账户 */}
      {accounts.length > 1 && (
        <div>
          <h4>切换账户</h4>
          {accounts.map((account) => (
            <button
              key={account.address}
              onClick={() => switchAccount({ account })}
              disabled={account.address === currentAccount?.address}
            >
              {account.address}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

查询链上数据

使用查询 Hooks 读取余额、对象、交易等链上信息,支持分页与细粒度字段选择。

查询余额

获取当前账户的 SUI 余额与币对象数量,适合展示资产概览。

tsx
import { useBalance, useCurrentAccount } from '@mysten/dapp-kit';

function Balance() {
  const account = useCurrentAccount();
  const { data: balance, isLoading, error } = useBalance({
    address: account?.address!,
  });

  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>错误: {error.message}</div>;
  if (!balance) return <div>无余额数据</div>;

  return (
    <div>
      <h3>SUI 余额</h3>
      <p>{(Number(balance.totalBalance) / 1_000_000_000).toFixed(2)} SUI</p>
      <p>币对象数量: {balance.coinObjectCount}</p>
    </div>
  );
}

查询所有代币余额

按代币类型列出账户持有的所有余额,用于资产明细与统计。

tsx
import { useSuiClientQuery, useCurrentAccount } from '@mysten/dapp-kit';

function AllBalances() {
  const account = useCurrentAccount();

  const { data, isLoading } = useSuiClientQuery('getAllBalances', {
    owner: account?.address!,
  });

  if (isLoading) return <div>加载中...</div>;

  return (
    <div>
      <h3>所有代币余额</h3>
      {data?.map((balance) => (
        <div key={balance.coinType}>
          <p>代币: {balance.coinType}</p>
          <p>余额: {balance.totalBalance}</p>
        </div>
      ))}
    </div>
  );
}

查询拥有的对象

列出账户拥有的对象集合,并按需展示类型、内容与 Display 字段。

tsx
import { useSuiClientQuery, useCurrentAccount } from '@mysten/dapp-kit';

function OwnedObjects() {
  const account = useCurrentAccount();

  const { data, isLoading, error } = useSuiClientQuery(
    'getOwnedObjects',
    {
      owner: account?.address!,
      options: {
        showType: true,
        showContent: true,
        showDisplay: true,
      },
    },
    {
      enabled: !!account,
    }
  );

  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>错误: {error.message}</div>;

  return (
    <div>
      <h3>拥有的对象</h3>
      {data?.data.map((obj) => (
        <div key={obj.data?.objectId}>
          <p>对象 ID: {obj.data?.objectId}</p>
          <p>类型: {obj.data?.type}</p>
          <p>版本: {obj.data?.version}</p>
        </div>
      ))}
    </div>
  );
}

查询对象详情

查询单个对象的完整详细信息,包括类型、内容、所有者与显示数据。

tsx
import { useSuiClientQuery } from '@mysten/dapp-kit';

function ObjectDetail({ objectId }: { objectId: string }) {
  const { data, isLoading } = useSuiClientQuery('getObject', {
    id: objectId,
    options: {
      showType: true,
      showContent: true,
      showOwner: true,
      showDisplay: true,
    },
  });

  if (isLoading) return <div>加载中...</div>;

  return (
    <div>
      <h3>对象详情</h3>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

查询交易历史

按地址过滤并分页获取交易记录,可查看时间、摘要与执行状态。

tsx
import { useSuiClientQuery, useCurrentAccount } from '@mysten/dapp-kit';

function TransactionHistory() {
  const account = useCurrentAccount();

  const { data, isLoading } = useSuiClientQuery('queryTransactionBlocks', {
    filter: {
      FromAddress: account?.address!,
    },
    options: {
      showEffects: true,
      showInput: true,
    },
    limit: 10,
  });

  if (isLoading) return <div>加载中...</div>;

  return (
    <div>
      <h3>交易历史</h3>
      {data?.data.map((tx) => (
        <div key={tx.digest}>
          <p>摘要: {tx.digest}</p>
          <p>时间: {new Date(Number(tx.timestampMs)).toLocaleString()}</p>
          <p>状态: {tx.effects?.status?.status}</p>
        </div>
      ))}
    </div>
  );
}

发送交易

使用 dApp Kit 的钱包钩子与交易 API 在前端发起并执行链上交易,涵盖转账、合约调用、仅签名与状态反馈等常见场景。

基础转账

最常见的场景:向指定地址转账 SUI。通过 Transaction 构建交易,由钱包签名并执行,支持多种交互方式。

tsx
import { Transaction } from '@mysten/sui/transactions';
import { useSignAndExecuteTransaction, useCurrentAccount } from '@mysten/dapp-kit';
import { useState } from 'react';

function Transfer() {
  const account = useCurrentAccount();
  const { mutate: signAndExecute } = useSignAndExecuteTransaction();
  // 可选:在初始化中配置统一回调
  // const { mutate: signAndExecute } = useSignAndExecuteTransaction({
  //   onSuccess: (result) => {
  //     console.log('交易成功:', result);
  //   },
  //   onError: (error) => {
  //     console.error('交易失败:', error);
  //   },
  // });

  const [recipient, setRecipient] = useState('');
  const [amount, setAmount] = useState('');

  const handleTransfer = () => {
    const tx = new Transaction();
    const mist = BigInt(Math.floor(parseFloat(amount || '0') * 1_000_000_000));
    const [coin] = tx.splitCoins(tx.gas, [tx.pure(mist)]);
    tx.transferObjects([coin], tx.pure.address(recipient));
    signAndExecute({ transaction: tx });
  };

  if (!account) return <div>请先连接钱包</div>;

  return (
    <div>
      <h3>转账 SUI</h3>
      <input type="text" placeholder="接收地址" value={recipient} onChange={(e) => setRecipient(e.target.value)} />
      <input type="number" placeholder="金额(SUI)" value={amount} onChange={(e) => setAmount(e.target.value)} />
      <button onClick={handleTransfer}>发送</button>
    </div>
  );
}

调用智能合约

调用 Move 模块中的函数执行业务逻辑(如铸造 NFT)。使用 tx.moveCall 传入参数,由钱包签名并执行,按需读取返回变化。

tsx
import { Transaction } from '@mysten/sui/transactions';
import { useSignAndExecuteTransaction } from '@mysten/dapp-kit';

function MintNFT({ packageId }: { packageId: string }) {
  const { mutate: signAndExecute } = useSignAndExecuteTransaction();

  const handleMint = (name: string, description: string, imageUrl: string) => {
    const tx = new Transaction();

    tx.moveCall({
      target: `${packageId}::nft::mint`,
      arguments: [
        tx.pure(name),
        tx.pure(description),
        tx.pure(imageUrl),
      ],
    });

    signAndExecute(
      {
        transaction: tx,
      },
      {
        onSuccess: (result) => {
          console.log('NFT 铸造成功:', result);

          // 获取创建的对象
          const createdObjects = result.objectChanges?.filter(
            (obj) => obj.type === 'created'
          );

          if (createdObjects && createdObjects.length > 0) {
            console.log('NFT ID:', createdObjects[0].objectId);
          }
        },
      }
    );
  };

  return (
    <div>
      <h3>铸造 NFT</h3>
      <button onClick={() => handleMint('My NFT', 'Description', 'https://...')}>
        铸造
      </button>
    </div>
  );
}

仅签名(不执行)

当需要后端或自定义执行流程时,仅请求钱包完成签名,保留 bytessignature,稍后在服务端或自定义客户端执行。

tsx
import { Transaction } from '@mysten/sui/transactions';
import { useSignTransaction } from '@mysten/dapp-kit';

function SignOnly() {
  const { mutateAsync: signTransaction } = useSignTransaction();

  const handleSign = async () => {
    const tx = new Transaction();
    // ... 构建交易

    try {
      const { signature, bytes } = await signTransaction({
        transaction: tx,
      });

      console.log('签名:', signature);
      console.log('交易字节:', bytes);

      // 可以将签名和交易发送给后端处理
    } catch (error) {
      console.error('签名失败:', error);
    }
  };

  return <button onClick={handleSign}>仅签名</button>;
}

带加载状态的交易

在执行期间呈现加载状态与按钮禁用,执行成功或失败后给出明确反馈,提升交互体验。

tsx
import { Transaction } from '@mysten/sui/transactions';
import { useSignAndExecuteTransaction } from '@mysten/dapp-kit';

function TransferWithLoading() {
  const { mutate: signAndExecute, isPending } = useSignAndExecuteTransaction();

  const handleTransfer = () => {
    const tx = new Transaction();
    // ... 构建交易

    signAndExecute(
      { transaction: tx },
      {
        onSuccess: (result) => {
          console.log('成功:', result);
        },
      }
    );
  };

  return (
    <button onClick={handleTransfer} disabled={isPending}>
      {isPending ? '处理中...' : '发送交易'}
    </button>
  );
}

React Hooks API

dApp Kit 提供钱包、查询与交易相关的 Hooks,统一管理状态与网络交互。

钱包 Hooks

用于连接/断开、读取当前钱包与账户、列出钱包与账户、切换账户等操作。

tsx
// 连接钱包
const { mutate: connect } = useConnectWallet();

// 断开钱包
const { mutate: disconnect } = useDisconnectWallet();

// 获取所有可用钱包
const wallets = useWallets();

// 获取当前账户
const account = useCurrentAccount();

// 获取当前钱包
const { currentWallet, connectionStatus } = useCurrentWallet();

// 获取所有账户
const accounts = useAccounts();

// 切换账户
const { mutate: switchAccount } = useSwitchAccount();

查询 Hooks

封装对 Sui 客户端的查询调用,支持传参、分页、缓存与加载/错误状态。

js
// 通用查询 Hook
const { data, isLoading, error } = useSuiClientQuery(
  'methodName',  // RPC 方法名
  { /* 参数 */ },
  { /* React Query 选项 */ }
);

// 查询余额
const { data: balance } = useBalance({
  address: '0x...',
});

// 无限滚动查询
const {
  data,
  fetchNextPage,
  hasNextPage,
  isLoading,
} = useSuiClientInfiniteQuery('getOwnedObjects', {
  owner: address,
  limit: 10,
});

交易 Hooks

用于签名并执行交易或仅签名,结合回调处理成功与失败结果。

tsx
// 签名并执行交易
const { mutate: signAndExecute, isPending } = useSignAndExecuteTransaction();

// 仅签名
const { mutateAsync: signTransaction } = useSignTransaction();

// 执行交易(使用已有签名)
const { mutate: executeTransaction } = useExecuteTransaction();

自定义 Hook 示例

根据业务需要组合与封装现有 Hooks,形成更易复用的应用级逻辑。

tsx
import { useSuiClientQuery, useCurrentAccount } from '@mysten/dapp-kit';

// 获取用户的 NFT
function useUserNFTs(packageId: string) {
  const account = useCurrentAccount();

  return useSuiClientQuery(
    'getOwnedObjects',
    {
      owner: account?.address!,
      filter: {
        StructType: `${packageId}::nft::NFT`,
      },
      options: {
        showContent: true,
        showDisplay: true,
      },
    },
    {
      enabled: !!account,
      refetchInterval: 10000, // 每 10 秒刷新
    }
  );
}

// 使用
function MyNFTs() {
  const { data, isLoading } = useUserNFTs('0xpackageId');

  if (isLoading) return <div>加载中...</div>;

  return (
    <div>
      {data?.data.map((nft) => (
        <div key={nft.data?.objectId}>
          {/* 渲染 NFT */}
        </div>
      ))}
    </div>
  );
}

完整示例

通过可运行的页面示例演示从连接到查询、交易的端到端流程。

NFT 市场 dApp

集成钱包与交易,支持查询上架、购买与持有的 NFT,展示常见交互模式。

tsx
import { useState } from 'react';
import {
  ConnectButton,
  useCurrentAccount,
  useSuiClientQuery,
  useSignAndExecuteTransaction,
} from '@mysten/dapp-kit';
import { Transaction } from '@mysten/sui/transactions';

const PACKAGE_ID = '0x...';

function NFTMarketplace() {
  const account = useCurrentAccount();
  const { mutate: signAndExecute } = useSignAndExecuteTransaction();

  // 查询市场上的 NFT
  const { data: marketNFTs, isLoading } = useSuiClientQuery('getOwnedObjects', {
    owner: PACKAGE_ID, // 市场合约地址
    filter: {
      StructType: `${PACKAGE_ID}::marketplace::Listing`,
    },
    options: {
      showContent: true,
      showDisplay: true,
    },
  });

  // 查询用户的 NFT
  const { data: userNFTs } = useSuiClientQuery(
    'getOwnedObjects',
    {
      owner: account?.address!,
      filter: {
        StructType: `${PACKAGE_ID}::nft::NFT`,
      },
      options: {
        showContent: true,
      },
    },
    {
      enabled: !!account,
    }
  );

  // 铸造 NFT
  const handleMint = () => {
    const tx = new Transaction();

    tx.moveCall({
      target: `${PACKAGE_ID}::nft::mint`,
      arguments: [
        tx.pure('My NFT'),
        tx.pure('NFT Description'),
        tx.pure('https://example.com/image.png'),
      ],
    });

    signAndExecute(
      { transaction: tx },
      {
        onSuccess: () => alert('NFT 铸造成功!'),
        onError: (error) => alert(`失败: ${error.message}`),
      }
    );
  };

  // 上架 NFT
  const handleList = (nftId: string, price: string) => {
    const tx = new Transaction();

    tx.moveCall({
      target: `${PACKAGE_ID}::marketplace::list`,
      arguments: [
        tx.object(nftId),
        tx.pure(BigInt(parseFloat(price) * 1_000_000_000)),
      ],
    });

    signAndExecute(
      { transaction: tx },
      {
        onSuccess: () => alert('NFT 上架成功!'),
      }
    );
  };

  // 购买 NFT
  const handleBuy = (listingId: string, price: string) => {
    const tx = new Transaction();

    const [coin] = tx.splitCoins(tx.gas, [
      tx.pure(BigInt(parseFloat(price) * 1_000_000_000))
    ]);

    tx.moveCall({
      target: `${PACKAGE_ID}::marketplace::buy`,
      arguments: [
        tx.object(listingId),
        coin,
      ],
    });

    signAndExecute(
      { transaction: tx },
      {
        onSuccess: () => alert('购买成功!'),
      }
    );
  };

  return (
    <div className="marketplace">
      <header>
        <h1>NFT 市场</h1>
        <ConnectButton />
      </header>

      {/* 市场 NFT */}
      <section>
        <h2>市场</h2>
        {isLoading ? (
          <div>加载中...</div>
        ) : (
          <div className="nft-grid">
            {marketNFTs?.data.map((listing) => (
              <div key={listing.data?.objectId} className="nft-card">
                <img src={listing.data?.display?.data?.image_url} alt="NFT" />
                <h3>{listing.data?.display?.data?.name}</h3>
                <p>{listing.data?.content?.fields?.price} SUI</p>
                <button onClick={() => handleBuy(
                  listing.data!.objectId,
                  listing.data!.content!.fields.price
                )}>
                  购买
                </button>
              </div>
            ))}
          </div>
        )}
      </section>

      {/* 我的 NFT */}
      {account && (
        <section>
          <h2>我的 NFT</h2>
          <button onClick={handleMint}>铸造新 NFT</button>

          <div className="nft-grid">
            {userNFTs?.data.map((nft) => (
              <div key={nft.data?.objectId} className="nft-card">
                <img src={nft.data?.display?.data?.image_url} alt="NFT" />
                <h3>{nft.data?.display?.data?.name}</h3>
                <button onClick={() => {
                  const price = prompt('输入价格 (SUI):');
                  if (price) handleList(nft.data!.objectId, price);
                }}>
                  上架出售
                </button>
              </div>
            ))}
          </div>
        </section>
      )}
    </div>
  );
}

export default NFTMarketplace;

最佳实践

汇总错误处理、加载状态、数据刷新与条件渲染等通用模式与建议。

错误处理

为交易与查询统一捕获错误并提示,结合重试与日志,帮助用户理解问题并快速恢复操作。

tsx
import { useSignAndExecuteTransaction } from '@mysten/dapp-kit';

function TransactionWithErrorHandling() {
  const { mutate: signAndExecute } = useSignAndExecuteTransaction();

  const handleTransaction = () => {
    const tx = new Transaction();
    // ... 构建交易

    signAndExecute(
      { transaction: tx },
      {
        onSuccess: (result) => {
          // 检查交易状态
          if (result.effects?.status?.status === 'success') {
            console.log('✅ 交易成功');
          } else {
            console.error('❌ 交易失败:', result.effects?.status);
          }
        },
        onError: (error) => {
          // 处理不同类型的错误
          if (error.message.includes('Rejected')) {
            alert('用户拒绝了交易');
          } else if (error.message.includes('insufficient')) {
            alert('余额不足');
          } else {
            alert(`交易失败: ${error.message}`);
          }
        },
      }
    );
  };

  return <button onClick={handleTransaction}>发送交易</button>;
}

加载状态管理

在查询与交易进行时展示加载与禁用状态,提升交互反馈与稳定性。

tsx
function TransactionButton() {
  const { mutate: signAndExecute, isPending, isSuccess, isError } =
    useSignAndExecuteTransaction();

  return (
    <div>
      <button onClick={() => signAndExecute({ transaction: tx })} disabled={isPending}>
        {isPending && '⏳ 处理中...'}
        {isSuccess && '✅ 成功'}
        {isError && '❌ 失败'}
        {!isPending && !isSuccess && !isError && '发送交易'}
      </button>
    </div>
  );
}

数据刷新

按需触发或自动进行数据刷新,确保界面展示与链上状态保持同步。

tsx
import { useSuiClientQuery } from '@mysten/dapp-kit';
import { useQueryClient } from '@tanstack/react-query';

function DataWithRefresh() {
  const queryClient = useQueryClient();

  const { data } = useSuiClientQuery('getBalance', {
    address: '0x...',
  });

  const handleRefresh = () => {
    // 刷新特定查询
    queryClient.invalidateQueries({ queryKey: ['getBalance'] });

    // 或刷新所有查询
    // queryClient.invalidateQueries();
  };

  return (
    <div>
      <p>余额: {data?.totalBalance}</p>
      <button onClick={handleRefresh}>刷新</button>
    </div>
  );
}

条件渲染

根据连接状态与余额等条件渲染不同内容,提供清晰的用户指引。

tsx
import { useCurrentAccount, useBalance } from '@mysten/dapp-kit';

function ConditionalContent() {
  const account = useCurrentAccount();
  const { data: balance } = useBalance({
    address: account?.address!,
  });

  // 未连接钱包
  if (!account) {
    return <div>请先连接钱包</div>;
  }

  // 余额不足
  if (balance && BigInt(balance.totalBalance) < 1_000_000_000n) {
    return <div>余额不足,请先充值</div>;
  }

  // 正常内容
  return <div>欢迎使用 dApp!</div>;
}

自动重连

应用挂载时尝试重连上次使用的钱包,并显示自动连接状态;需在 WalletProvider 启用 autoConnect 才会生效。

typescript
import { useAutoConnectWallet } from '@mysten/dapp-kit';

function App() {
  // 自动连接上次使用的钱包
  useAutoConnectWallet();

  return <YourApp />;
}

常见问题

总结常见配置与使用疑问,并给出实用的解决思路与参考示例。

Q1: 如何支持多个网络?

A: 配置多个网络并允许用户切换:

tsx
import { SuiClientProvider } from '@mysten/dapp-kit';
import { getFullnodeUrl } from '@mysten/sui/client';

const networks = {
  devnet: { url: getFullnodeUrl('devnet') },
  testnet: { url: getFullnodeUrl('testnet') },
  mainnet: { url: getFullnodeUrl('mainnet') },
};

function App() {
  const [network, setNetwork] = useState<keyof typeof networks>('devnet');

  return (
    <SuiClientProvider networks={networks} defaultNetwork={network}>
      <select value={network} onChange={(e) => setNetwork(e.target.value as any)}>
        <option value="devnet">Devnet</option>
        <option value="testnet">Testnet</option>
        <option value="mainnet">Mainnet</option>
      </select>

      <WalletProvider>
        <YourApp />
      </WalletProvider>
    </SuiClientProvider>
  );
}

Q2: 如何处理钱包未安装?

A: 检查钱包安装状态:

tsx
import { useWallets } from '@mysten/dapp-kit';

function WalletList() {
  const wallets = useWallets();

  return (
    <div>
      {wallets.map((wallet) => (
        <div key={wallet.name}>
          {wallet.isInstalled ? (
            <button onClick={() => connect({ wallet })}>
              连接 {wallet.name}
            </button>
          ) : (
            <a href={wallet.downloadUrl} target="_blank" rel="noreferrer">
              安装 {wallet.name}
            </a>
          )}
        </div>
      ))}
    </div>
  );
}

Q3: 如何显示交易进度?

A: 使用加载状态和回调:

tsx
function TransactionWithProgress() {
  const [status, setStatus] = useState<'idle' | 'signing' | 'executing' | 'success' | 'error'>('idle');
  const { mutate: signAndExecute } = useSignAndExecuteTransaction();

  const handleTransaction = () => {
    setStatus('signing');

    signAndExecute(
      { transaction: tx },
      {
        onSuccess: (result) => {
          setStatus('executing');

          // 等待交易确认
          setTimeout(() => {
            setStatus('success');
          }, 2000);
        },
        onError: () => {
          setStatus('error');
        },
      }
    );
  };

  return (
    <div>
      {status === 'idle' && <button onClick={handleTransaction}>发送</button>}
      {status === 'signing' && <div>⏳ 请在钱包中签名...</div>}
      {status === 'executing' && <div>⏳ 交易执行中...</div>}
      {status === 'success' && <div>✅ 交易成功!</div>}
      {status === 'error' && <div>❌ 交易失败</div>}
    </div>
  );
}

基于 MIT 许可发布