MultiversXdApp前端集成Skill multiversx-dapp-frontend

这个技能用于将 React 前端应用快速集成到 MultiversX 区块链,支持钱包认证、交易签名、智能合约交互和 Web3 功能。适用于构建去中心化应用(dApp)、DeFi 平台、NFT 市场等场景。关键词:MultiversX,区块链,React,dApp 开发,钱包连接,智能合约,Web3。

DApp开发 0 次安装 0 次浏览 更新于 3/21/2026

name: multiversx-dapp-frontend description: 将 React 应用适配到 MultiversX 区块链,支持钱包连接、交易和智能合约交互。当应用需要 Web3、区块链、钱包登录、加密货币支付、NFT、代币或智能合约调用时使用。

MultiversX dApp 前端集成

将 React 应用转换为 MultiversX 区块链 dApp,实现钱包认证、交易签名和智能合约交互。

先决条件

起始项目已包含 MultiversX SDK 包:

  • @multiversx/sdk-core - 核心区块链原语
  • @multiversx/sdk-dapp - React hooks 和 providers
  • @multiversx/sdk-dapp-core-ui - 预构建 UI 组件
  • @multiversx/sdk-dapp-utils - 工具函数

应用初始化

关键: 在渲染 React 应用之前调用 initApp()

修改 src/main.tsx

import { createRoot } from 'react-dom/client';
import { initApp } from '@multiversx/sdk-dapp/out/methods/initApp/initApp';
import { EnvironmentsEnum } from '@multiversx/sdk-dapp/out/types/enums.types';
import App from './App';
import './index.css';

const config = {
  storage: {
    getStorageCallback: () => sessionStorage
  },
  dAppConfig: {
    environment: EnvironmentsEnum.devnet, // 更改为 testnet 或 mainnet
    nativeAuth: {
      expirySeconds: 86400, // 24 小时
      tokenExpirationToastWarningSeconds: 300 // 5 分钟警告
    }
  }
};

initApp(config).then(() => {
  const root = createRoot(document.getElementById('root')!);
  root.render(<App />);
});

环境选项

环境 使用场景
EnvironmentsEnum.devnet 开发 使用免费代币测试
EnvironmentsEnum.testnet 测试 预生产测试
EnvironmentsEnum.mainnet 生产 真实交易

钱包连接与 UnlockPanelManager

UnlockPanelManager 提供一个预构建 UI,支持所有钱包提供者。

设置解锁面板

import { UnlockPanelManager } from '@multiversx/sdk-dapp/out/managers/UnlockPanelManager';
import { useNavigate } from 'react-router-dom';

function ConnectWalletButton() {
  const navigate = useNavigate();

  const handleConnect = () => {
    const unlockPanelManager = UnlockPanelManager.init({
      loginHandler: () => {
        navigate('/dashboard'); // 登录成功后重定向
      },
      onClose: () => {
        // 可选:处理未登录关闭面板
      }
    });

    unlockPanelManager.openUnlockPanel();
  };

  return (
    <button onClick={handleConnect}>
      连接钱包
    </button>
  );
}

支持的钱包提供者

UnlockPanelManager 自动显示所有可用提供者:

  • xPortal App - 通过二维码的移动钱包
  • xPortal Hub - 基于浏览器的 xPortal
  • MultiversX DeFi Wallet - 浏览器扩展
  • Web Wallet - MultiversX 网页钱包
  • Ledger - 硬件钱包
  • WalletConnect - WalletConnect v2 协议
  • Passkey - WebAuthn/FIDO2 认证

登出

import { getAccountProvider } from '@multiversx/sdk-dapp/out/providers/accountProvider';

async function handleLogout() {
  const provider = getAccountProvider();
  await provider.logout();
  navigate('/'); // 重定向到首页
}

认证状态钩子

检查登录状态

import { useGetIsLoggedIn } from '@multiversx/sdk-dapp/out/hooks/account/useGetIsLoggedIn';

function AuthStatus() {
  const isLoggedIn = useGetIsLoggedIn();

  return isLoggedIn ? <Dashboard /> : <LoginPrompt />;
}

获取账户数据

import { useGetAccount } from '@multiversx/sdk-dapp/out/hooks/account/useGetAccount';

function AccountInfo() {
  const { address, balance, nonce } = useGetAccount();

  return (
    <div>
      <p>地址: {address}</p>
      <p>余额: {balance} EGLD</p>
    </div>
  );
}

仅获取地址

import { useGetAddress } from '@multiversx/sdk-dapp/out/hooks/account/useGetAddress';

function AddressDisplay() {
  const address = useGetAddress();
  return <span>{address}</span>;
}

获取网络配置

import { useGetNetworkConfig } from '@multiversx/sdk-dapp/out/hooks/useGetNetworkConfig';

function NetworkInfo() {
  const { network } = useGetNetworkConfig();

  return (
    <div>
      <p>链 ID: {network.chainId}</p>
      <p>标签: {network.egldLabel}</p>
    </div>
  );
}

受保护路由

创建认证守卫组件用于保护页面:

import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useGetIsLoggedIn } from '@multiversx/sdk-dapp/out/hooks/account/useGetIsLoggedIn';

interface AuthGuardProps {
  children: React.ReactNode;
}

function AuthGuard({ children }: AuthGuardProps) {
  const isLoggedIn = useGetIsLoggedIn();
  const navigate = useNavigate();
  const [isChecking, setIsChecking] = useState(true);

  useEffect(() => {
    // 小延迟以允许 SDK 恢复会话
    const timer = setTimeout(() => {
      setIsChecking(false);
      if (!isLoggedIn) {
        navigate('/');
      }
    }, 100);

    return () => clearTimeout(timer);
  }, [isLoggedIn, navigate]);

  if (isChecking) {
    return <div>加载中...</div>;
  }

  return isLoggedIn ? <>{children}</> : null;
}

// 在路由中使用
<Route path="/dashboard" element={
  <AuthGuard>
    <Dashboard />
  </AuthGuard>
} />

基本交易

发送 EGLD 转账

import { Transaction, Address } from '@multiversx/sdk-core';
import { getAccountProvider } from '@multiversx/sdk-dapp/out/providers/accountProvider';
import { refreshAccount } from '@multiversx/sdk-dapp/out/utils/account/refreshAccount';
import { TransactionManager } from '@multiversx/sdk-dapp/out/managers/TransactionManager';
import { useGetAccount } from '@multiversx/sdk-dapp/out/hooks/account/useGetAccount';
import { useGetNetworkConfig } from '@multiversx/sdk-dapp/out/hooks/useGetNetworkConfig';

function SendEgld() {
  const { address: senderAddress, nonce } = useGetAccount();
  const { network } = useGetNetworkConfig();

  const sendTransaction = async (receiverAddress: string, amount: string) => {
    // 刷新账户以获取最新 nonce
    await refreshAccount();

    // 创建交易(金额以 wei 为单位:1 EGLD = 10^18)
    const transaction = new Transaction({
      value: BigInt(amount),
      receiver: Address.newFromBech32(receiverAddress),
      sender: Address.newFromBech32(senderAddress),
      gasLimit: BigInt(50000),
      chainID: network.chainId,
      nonce: BigInt(nonce)
    });

    // 签名交易
    const provider = getAccountProvider();
    const signedTransactions = await provider.signTransactions([transaction]);

    // 发送和跟踪
    const txManager = TransactionManager.getInstance();
    const sentTransactions = await txManager.send(signedTransactions);

    // 通过 toast 通知跟踪
    const sessionId = await txManager.track(sentTransactions, {
      transactionsDisplayInfo: {
        processingMessage: '发送 EGLD 中...',
        successMessage: 'EGLD 发送成功!',
        errorMessage: '交易失败'
      }
    });

    return sessionId;
  };

  return (
    <button onClick={() => sendTransaction('erd1...', '1000000000000000000')}>
      发送 1 EGLD
    </button>
  );
}

发送 ESDT 代币转账

import { Transaction, Address, TokenTransfer } from '@multiversx/sdk-core';
import { EsdtHelpers } from '@multiversx/sdk-dapp-utils';

async function sendToken(
  receiverAddress: string,
  tokenId: string,
  amount: string,
  decimals: number
) {
  await refreshAccount();
  const { address: senderAddress, nonce } = useGetAccount();
  const { network } = useGetNetworkConfig();

  // 构建代币转账数据
  const transfer = TokenTransfer.fungibleFromAmount(tokenId, amount, decimals);

  const transaction = new Transaction({
    value: BigInt(0),
    receiver: Address.newFromBech32(receiverAddress),
    sender: Address.newFromBech32(senderAddress),
    gasLimit: BigInt(500000),
    chainID: network.chainId,
    nonce: BigInt(nonce),
    data: Buffer.from(`ESDTTransfer@${Buffer.from(tokenId).toString('hex')}@${transfer.amountAsBigInteger.toString(16)}`)
  });

  const provider = getAccountProvider();
  const signedTransactions = await provider.signTransactions([transaction]);

  const txManager = TransactionManager.getInstance();
  await txManager.send(signedTransactions);
}

交易状态跟踪

监控待处理交易

import { useGetPendingTransactions } from '@multiversx/sdk-dapp/out/hooks/transactions/useGetPendingTransactions';

function PendingTransactions() {
  const { pendingTransactions, hasPendingTransactions } = useGetPendingTransactions();

  if (!hasPendingTransactions) {
    return <p>没有待处理交易</p>;
  }

  return (
    <ul>
      {pendingTransactions.map((tx) => (
        <li key={tx.hash}>处理中: {tx.hash}</li>
      ))}
    </ul>
  );
}

监控成功交易

import { useGetSuccessfulTransactions } from '@multiversx/sdk-dapp/out/hooks/transactions/useGetSuccessfulTransactions';

function SuccessfulTransactions() {
  const { successfulTransactions } = useGetSuccessfulTransactions();

  return (
    <ul>
      {successfulTransactions.map((tx) => (
        <li key={tx.hash}>已完成: {tx.hash}</li>
      ))}
    </ul>
  );
}

监控失败交易

import { useGetFailedTransactions } from '@multiversx/sdk-dapp/out/hooks/transactions/useGetFailedTransactions';

function FailedTransactions() {
  const { failedTransactions } = useGetFailedTransactions();

  return (
    <ul>
      {failedTransactions.map((tx) => (
        <li key={tx.hash}>失败: {tx.hash}</li>
      ))}
    </ul>
  );
}

高级智能合约交互

加载 ABI 文件

将 ABI JSON 文件存储在 src/contracts/ 并导入:

// src/contracts/myContract.abi.json - 在此放置您的 ABI 文件

import { AbiRegistry, SmartContract, Address } from '@multiversx/sdk-core';
import myContractAbi from '@/contracts/myContract.abi.json';

function createContract(contractAddress: string) {
  const abiRegistry = AbiRegistry.create(myContractAbi);

  return new SmartContract({
    address: Address.newFromBech32(contractAddress),
    abi: abiRegistry
  });
}

查询智能合约(视图函数)

import { ApiNetworkProvider } from '@multiversx/sdk-core';
import { SmartContract, Address, AbiRegistry, ResultsParser } from '@multiversx/sdk-core';

async function queryContract() {
  const networkProvider = new ApiNetworkProvider('https://devnet-api.multiversx.com');

  const contract = new SmartContract({
    address: Address.newFromBech32('erd1qqqqqqqqqqqqqq...'),
    abi: AbiRegistry.create(myContractAbi)
  });

  // 为视图函数创建查询
  const query = contract.createQuery({
    func: 'getTokenPrice',
    args: [] // 如果需要,添加参数
  });

  // 执行查询
  const queryResponse = await networkProvider.queryContract(query);

  // 使用 ABI 解析结果
  const resultsParser = new ResultsParser();
  const endpointDefinition = contract.getEndpoint('getTokenPrice');
  const parsed = resultsParser.parseQueryResponse(queryResponse, endpointDefinition);

  return parsed.values[0]; // 第一个返回值
}

调用智能合约(状态改变函数)

import { SmartContract, Address, AbiRegistry, TokenTransfer } from '@multiversx/sdk-core';
import { getAccountProvider } from '@multiversx/sdk-dapp/out/providers/accountProvider';
import { refreshAccount } from '@multiversx/sdk-dapp/out/utils/account/refreshAccount';
import { TransactionManager } from '@multiversx/sdk-dapp/out/managers/TransactionManager';

async function callContract(
  contractAddress: string,
  functionName: string,
  args: any[],
  egldValue?: string
) {
  await refreshAccount();

  const contract = new SmartContract({
    address: Address.newFromBech32(contractAddress),
    abi: AbiRegistry.create(myContractAbi)
  });

  // 构建交互
  const interaction = contract.methods[functionName](args);

  // 设置 gas 限制
  interaction.withGasLimit(BigInt(10000000));

  // 如果需要,添加 EGLD 支付
  if (egldValue) {
    interaction.withValue(BigInt(egldValue));
  }

  // 构建交易
  const transaction = interaction.buildTransaction();

  // 签名
  const provider = getAccountProvider();
  const signedTransactions = await provider.signTransactions([transaction]);

  // 发送和跟踪
  const txManager = TransactionManager.getInstance();
  const sent = await txManager.send(signedTransactions);

  await txManager.track(sent, {
    transactionsDisplayInfo: {
      processingMessage: `调用 ${functionName} 中...`,
      successMessage: '交易成功!',
      errorMessage: '交易失败'
    }
  });
}

批量/多调用交易

并行发送多个交易:

async function batchTransactions(transactions: Transaction[]) {
  const provider = getAccountProvider();
  const signedTransactions = await provider.signTransactions(transactions);

  const txManager = TransactionManager.getInstance();

  // 并行发送所有
  const sent = await txManager.send(signedTransactions);

  await txManager.track(sent, {
    transactionsDisplayInfo: {
      processingMessage: '处理批量交易中...',
      successMessage: '所有交易完成!',
      errorMessage: '部分交易失败'
    }
  });
}

顺序发送交易(等待每个完成):

async function sequentialTransactions(transactions: Transaction[]) {
  const provider = getAccountProvider();
  const signedTransactions = await provider.signTransactions(transactions);

  const txManager = TransactionManager.getInstance();

  // 使用嵌套数组顺序发送
  const sent = await txManager.send(signedTransactions.map(tx => [tx]));

  await txManager.track(sent);
}

智能合约与代币支付

async function callWithTokenPayment(
  contractAddress: string,
  functionName: string,
  tokenId: string,
  amount: string,
  decimals: number
) {
  const contract = new SmartContract({
    address: Address.newFromBech32(contractAddress),
    abi: AbiRegistry.create(myContractAbi)
  });

  const payment = TokenTransfer.fungibleFromAmount(tokenId, amount, decimals);

  const interaction = contract.methods[functionName]([])
    .withSingleESDTTransfer(payment)
    .withGasLimit(BigInt(15000000));

  const transaction = interaction.buildTransaction();

  // 签名和发送...
}

UI 组件

格式化代币金额

import { MvxFormatAmount } from '@multiversx/sdk-dapp-core-ui';

function BalanceDisplay({ amount }: { amount: string }) {
  return (
    <MvxFormatAmount
      value={amount}
      decimals={18}
      egldLabel="EGLD"
      showLabel
    />
  );
}

显示交易表

import { MvxTransactionsTable } from '@multiversx/sdk-dapp-core-ui';

function TransactionsPage() {
  return (
    <div>
      <h2>您的交易</h2>
      <MvxTransactionsTable />
    </div>
  );
}

关键知识

错误:硬编码链 ID

// 错误 - 在不同网络上会中断
const transaction = new Transaction({
  chainID: 'D' // 硬编码 devnet
});

正确:使用网络配置

// 正确 - 动态链 ID
const { network } = useGetNetworkConfig();
const transaction = new Transaction({
  chainID: network.chainId
});

错误:交易前未刷新账户

// 错误 - 可能使用过时的 nonce
const { nonce } = useGetAccount();
const tx = new Transaction({ nonce: BigInt(nonce) });

正确:始终先刷新账户

// 正确 - 新鲜 nonce
await refreshAccount();
const { nonce } = useGetAccount();
const tx = new Transaction({ nonce: BigInt(nonce) });

错误:gas 限制不足

// 错误 - 50,000 gas 仅适用于简单 EGLD 转账
const tx = new Transaction({
  gasLimit: BigInt(50000),
  data: Buffer.from('ESDTTransfer@...') // 代币转账需要更多 gas!
});

正确:适当的 gas 限制

// 按操作类型正确的 gas 限制:
// 简单 EGLD 转账:50,000
// ESDT 代币转账:500,000
// NFT 转账:1,000,000
// 智能合约调用:5,000,000 - 60,000,000(取决于复杂性)

const tx = new Transaction({
  gasLimit: BigInt(500000) // 用于代币转账
});

错误:在 initApp 完成前渲染

// 错误 - SDK 未初始化
createRoot(document.getElementById('root')!).render(<App />);
initApp(config); // 太晚了!

正确:等待 initApp

// 正确 - 渲染前 SDK 已准备
initApp(config).then(() => {
  createRoot(document.getElementById('root')!).render(<App />);
});

API 端点参考

网络 API URL
开发网 https://devnet-api.multiversx.com
测试网 https://testnet-api.multiversx.com
主网 https://api.multiversx.com

SDK 文档链接

验证清单

完成前验证:

  • [ ] initApp() 在 main.tsx 中调用,在 createRoot().render() 之前
  • [ ] 环境设置正确(devnet/testnet/mainnet)
  • [ ] 钱包连接与 UnlockPanelManager 正常工作
  • [ ] useGetIsLoggedIn() 正确检测认证状态
  • [ ] 受保护路由重定向未认证用户
  • [ ] refreshAccount() 在创建交易前调用
  • [ ] gas 限制适合交易类型
  • [ ] 链 ID 来自 useGetNetworkConfig(),而非硬编码
  • [ ] 交易跟踪显示 toast 通知
  • [ ] 智能合约 ABI 正确加载(如果使用合约)