multiversx-dapp-frontend
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMultiversX dApp Frontend Integration
MultiversX dApp前端集成
Transform React applications into MultiversX blockchain dApps with wallet authentication, transaction signing, and smart contract interactions.
将React应用转换为支持MultiversX区块链的dApp,实现钱包认证、交易签名及智能合约交互。
Prerequisites
前置条件
The starter project already includes MultiversX SDK packages:
- - Core blockchain primitives
@multiversx/sdk-core - - React hooks and providers
@multiversx/sdk-dapp - - Pre-built UI components
@multiversx/sdk-dapp-core-ui - - Utility functions
@multiversx/sdk-dapp-utils
初始项目已包含以下MultiversX SDK包:
- - 核心区块链原语
@multiversx/sdk-core - - React钩子与提供者
@multiversx/sdk-dapp - - 预构建UI组件
@multiversx/sdk-dapp-core-ui - - 工具函数
@multiversx/sdk-dapp-utils
App Initialization
应用初始化
CRITICAL: Call BEFORE rendering the React application.
initApp()Modify :
src/main.tsxtypescript
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, // Change to testnet or mainnet
nativeAuth: {
expirySeconds: 86400, // 24 hours
tokenExpirationToastWarningSeconds: 300 // 5 min warning
}
}
};
initApp(config).then(() => {
const root = createRoot(document.getElementById('root')!);
root.render(<App />);
});重要提示: 在渲染React应用之前必须调用。
initApp()修改:
src/main.tsxtypescript
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 />);
});Environment Options
环境选项
| Environment | Value | Use Case |
|---|---|---|
| Development | Testing with free tokens |
| Staging | Pre-production testing |
| Production | Real transactions |
| 环境 | 取值 | 使用场景 |
|---|---|---|
| 开发环境 | 使用免费代币进行测试 |
| 预发布环境 | 生产前测试 |
| 生产环境 | 真实交易场景 |
Wallet Connection with UnlockPanelManager
借助UnlockPanelManager实现钱包连接
UnlockPanelManager provides a pre-built UI supporting ALL wallet providers.
UnlockPanelManager提供预构建UI,支持所有钱包提供商。
Setup Unlock Panel
设置解锁面板
typescript
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'); // Redirect after successful login
},
onClose: () => {
// Optional: handle panel close without login
}
});
unlockPanelManager.openUnlockPanel();
};
return (
<button onClick={handleConnect}>
Connect Wallet
</button>
);
}typescript
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>
);
}Supported Wallet Providers
支持的钱包提供商
UnlockPanelManager automatically shows all available providers:
- xPortal App - Mobile wallet via QR code
- xPortal Hub - Browser-based xPortal
- MultiversX DeFi Wallet - Browser extension
- Web Wallet - MultiversX web wallet
- Ledger - Hardware wallet
- WalletConnect - WalletConnect v2 protocol
- Passkey - WebAuthn/FIDO2 authentication
UnlockPanelManager自动显示所有可用提供商:
- xPortal App - 移动端钱包,通过二维码连接
- xPortal Hub - 浏览器端xPortal
- MultiversX DeFi Wallet - 浏览器扩展钱包
- Web Wallet - MultiversX网页钱包
- Ledger - 硬件钱包
- WalletConnect - WalletConnect v2协议
- Passkey - WebAuthn/FIDO2认证
Logout
登出
typescript
import { getAccountProvider } from '@multiversx/sdk-dapp/out/providers/accountProvider';
async function handleLogout() {
const provider = getAccountProvider();
await provider.logout();
navigate('/'); // Redirect to home
}typescript
import { getAccountProvider } from '@multiversx/sdk-dapp/out/providers/accountProvider';
async function handleLogout() {
const provider = getAccountProvider();
await provider.logout();
navigate('/'); // 重定向至首页
}Authentication State Hooks
认证状态钩子
Check Login Status
检查登录状态
typescript
import { useGetIsLoggedIn } from '@multiversx/sdk-dapp/out/hooks/account/useGetIsLoggedIn';
function AuthStatus() {
const isLoggedIn = useGetIsLoggedIn();
return isLoggedIn ? <Dashboard /> : <LoginPrompt />;
}typescript
import { useGetIsLoggedIn } from '@multiversx/sdk-dapp/out/hooks/account/useGetIsLoggedIn';
function AuthStatus() {
const isLoggedIn = useGetIsLoggedIn();
return isLoggedIn ? <Dashboard /> : <LoginPrompt />;
}Get Account Data
获取账户数据
typescript
import { useGetAccount } from '@multiversx/sdk-dapp/out/hooks/account/useGetAccount';
function AccountInfo() {
const { address, balance, nonce } = useGetAccount();
return (
<div>
<p>Address: {address}</p>
<p>Balance: {balance} EGLD</p>
</div>
);
}typescript
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>
);
}Get Address Only
仅获取地址
typescript
import { useGetAddress } from '@multiversx/sdk-dapp/out/hooks/account/useGetAddress';
function AddressDisplay() {
const address = useGetAddress();
return <span>{address}</span>;
}typescript
import { useGetAddress } from '@multiversx/sdk-dapp/out/hooks/account/useGetAddress';
function AddressDisplay() {
const address = useGetAddress();
return <span>{address}</span>;
}Get Network Configuration
获取网络配置
typescript
import { useGetNetworkConfig } from '@multiversx/sdk-dapp/out/hooks/useGetNetworkConfig';
function NetworkInfo() {
const { network } = useGetNetworkConfig();
return (
<div>
<p>Chain ID: {network.chainId}</p>
<p>Label: {network.egldLabel}</p>
</div>
);
}typescript
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>
);
}Protected Routes
受保护路由
Create an auth guard component for protected pages:
typescript
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(() => {
// Small delay to allow SDK to restore session
const timer = setTimeout(() => {
setIsChecking(false);
if (!isLoggedIn) {
navigate('/');
}
}, 100);
return () => clearTimeout(timer);
}, [isLoggedIn, navigate]);
if (isChecking) {
return <div>Loading...</div>;
}
return isLoggedIn ? <>{children}</> : null;
}
// Usage in routes
<Route path="/dashboard" element={
<AuthGuard>
<Dashboard />
</AuthGuard>
} />创建认证守卫组件用于保护页面:
typescript
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>
} />Basic Transactions
基础交易
Send EGLD Transfer
发送EGLD转账
typescript
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) => {
// Refresh account to get latest nonce
await refreshAccount();
// Create transaction (amount in 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)
});
// Sign transaction
const provider = getAccountProvider();
const signedTransactions = await provider.signTransactions([transaction]);
// Send and track
const txManager = TransactionManager.getInstance();
const sentTransactions = await txManager.send(signedTransactions);
// Track with toast notifications
const sessionId = await txManager.track(sentTransactions, {
transactionsDisplayInfo: {
processingMessage: 'Sending EGLD...',
successMessage: 'EGLD sent successfully!',
errorMessage: 'Transaction failed'
}
});
return sessionId;
};
return (
<button onClick={() => sendTransaction('erd1...', '1000000000000000000')}>
Send 1 EGLD
</button>
);
}typescript
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);
// 通过吐司通知追踪状态
const sessionId = await txManager.track(sentTransactions, {
transactionsDisplayInfo: {
processingMessage: '正在发送EGLD...',
successMessage: 'EGLD发送成功!',
errorMessage: '交易失败'
}
});
return sessionId;
};
return (
<button onClick={() => sendTransaction('erd1...', '1000000000000000000')}>
发送1 EGLD
</button>
);
}Send ESDT Token Transfer
发送ESDT代币转账
typescript
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();
// Build token transfer data
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);
}typescript
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);
}Transaction Status Tracking
交易状态追踪
Monitor Pending Transactions
监控待处理交易
typescript
import { useGetPendingTransactions } from '@multiversx/sdk-dapp/out/hooks/transactions/useGetPendingTransactions';
function PendingTransactions() {
const { pendingTransactions, hasPendingTransactions } = useGetPendingTransactions();
if (!hasPendingTransactions) {
return <p>No pending transactions</p>;
}
return (
<ul>
{pendingTransactions.map((tx) => (
<li key={tx.hash}>Processing: {tx.hash}</li>
))}
</ul>
);
}typescript
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>
);
}Monitor Successful Transactions
监控成功交易
typescript
import { useGetSuccessfulTransactions } from '@multiversx/sdk-dapp/out/hooks/transactions/useGetSuccessfulTransactions';
function SuccessfulTransactions() {
const { successfulTransactions } = useGetSuccessfulTransactions();
return (
<ul>
{successfulTransactions.map((tx) => (
<li key={tx.hash}>Completed: {tx.hash}</li>
))}
</ul>
);
}typescript
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>
);
}Monitor Failed Transactions
监控失败交易
typescript
import { useGetFailedTransactions } from '@multiversx/sdk-dapp/out/hooks/transactions/useGetFailedTransactions';
function FailedTransactions() {
const { failedTransactions } = useGetFailedTransactions();
return (
<ul>
{failedTransactions.map((tx) => (
<li key={tx.hash}>Failed: {tx.hash}</li>
))}
</ul>
);
}typescript
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>
);
}Advanced Smart Contract Interactions
高级智能合约交互
Loading ABI Files
加载ABI文件
Store ABI JSON files in and import them:
src/contracts/typescript
// src/contracts/myContract.abi.json - place your ABI file here
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
});
}将ABI JSON文件存储在目录并导入:
src/contracts/typescript
// 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
});
}Query Smart Contract (View Functions)
查询智能合约(视图函数)
typescript
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)
});
// Create query for a view function
const query = contract.createQuery({
func: 'getTokenPrice',
args: [] // Add arguments if needed
});
// Execute query
const queryResponse = await networkProvider.queryContract(query);
// Parse result using ABI
const resultsParser = new ResultsParser();
const endpointDefinition = contract.getEndpoint('getTokenPrice');
const parsed = resultsParser.parseQueryResponse(queryResponse, endpointDefinition);
return parsed.values[0]; // First return value
}typescript
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]; // 返回第一个结果值
}Call Smart Contract (State-Changing Functions)
调用智能合约(状态变更函数)
typescript
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)
});
// Build interaction
const interaction = contract.methods[functionName](args);
// Set gas limit
interaction.withGasLimit(BigInt(10000000));
// Add EGLD payment if needed
if (egldValue) {
interaction.withValue(BigInt(egldValue));
}
// Build transaction
const transaction = interaction.buildTransaction();
// Sign
const provider = getAccountProvider();
const signedTransactions = await provider.signTransactions([transaction]);
// Send and track
const txManager = TransactionManager.getInstance();
const sent = await txManager.send(signedTransactions);
await txManager.track(sent, {
transactionsDisplayInfo: {
processingMessage: `Calling ${functionName}...`,
successMessage: 'Transaction successful!',
errorMessage: 'Transaction failed'
}
});
}typescript
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: '交易失败'
}
});
}Batch/Multi-Call Transactions
批量/多调用交易
Send multiple transactions in parallel:
typescript
async function batchTransactions(transactions: Transaction[]) {
const provider = getAccountProvider();
const signedTransactions = await provider.signTransactions(transactions);
const txManager = TransactionManager.getInstance();
// Send all in parallel
const sent = await txManager.send(signedTransactions);
await txManager.track(sent, {
transactionsDisplayInfo: {
processingMessage: 'Processing batch...',
successMessage: 'All transactions completed!',
errorMessage: 'Some transactions failed'
}
});
}Send transactions sequentially (wait for each to complete):
typescript
async function sequentialTransactions(transactions: Transaction[]) {
const provider = getAccountProvider();
const signedTransactions = await provider.signTransactions(transactions);
const txManager = TransactionManager.getInstance();
// Send sequentially using nested arrays
const sent = await txManager.send(signedTransactions.map(tx => [tx]));
await txManager.track(sent);
}并行发送多笔交易:
typescript
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: '部分交易失败'
}
});
}顺序发送交易(等待每笔交易完成后再发送下一笔):
typescript
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);
}Smart Contract with Token Payment
附带代币支付的智能合约调用
typescript
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();
// Sign and send...
}typescript
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 Components
UI组件
Format Token Amounts
格式化代币金额
typescript
import { MvxFormatAmount } from '@multiversx/sdk-dapp-core-ui';
function BalanceDisplay({ amount }: { amount: string }) {
return (
<MvxFormatAmount
value={amount}
decimals={18}
egldLabel="EGLD"
showLabel
/>
);
}typescript
import { MvxFormatAmount } from '@multiversx/sdk-dapp-core-ui';
function BalanceDisplay({ amount }: { amount: string }) {
return (
<MvxFormatAmount
value={amount}
decimals={18}
egldLabel="EGLD"
showLabel
/>
);
}Display Transactions Table
显示交易表格
typescript
import { MvxTransactionsTable } from '@multiversx/sdk-dapp-core-ui';
function TransactionsPage() {
return (
<div>
<h2>Your Transactions</h2>
<MvxTransactionsTable />
</div>
);
}typescript
import { MvxTransactionsTable } from '@multiversx/sdk-dapp-core-ui';
function TransactionsPage() {
return (
<div>
<h2>您的交易记录</h2>
<MvxTransactionsTable />
</div>
);
}Critical Knowledge
关键注意事项
WRONG: Hardcoding chain ID
错误做法:硬编码链ID
typescript
// WRONG - Will break on different networks
const transaction = new Transaction({
chainID: 'D' // Hardcoded devnet
});typescript
// 错误 - 在不同网络下会失效
const transaction = new Transaction({
chainID: 'D' // 硬编码为devnet
});CORRECT: Use network config
正确做法:使用网络配置
typescript
// CORRECT - Dynamic chain ID
const { network } = useGetNetworkConfig();
const transaction = new Transaction({
chainID: network.chainId
});typescript
// 正确 - 动态获取链ID
const { network } = useGetNetworkConfig();
const transaction = new Transaction({
chainID: network.chainId
});WRONG: Not refreshing account before transaction
错误做法:创建交易前不刷新账户
typescript
// WRONG - May use stale nonce
const { nonce } = useGetAccount();
const tx = new Transaction({ nonce: BigInt(nonce) });typescript
// 错误 - 可能使用过期的nonce
const { nonce } = useGetAccount();
const tx = new Transaction({ nonce: BigInt(nonce) });CORRECT: Always refresh account first
正确做法:创建交易前先刷新账户
typescript
// CORRECT - Fresh nonce
await refreshAccount();
const { nonce } = useGetAccount();
const tx = new Transaction({ nonce: BigInt(nonce) });typescript
// 正确 - 获取最新nonce
await refreshAccount();
const { nonce } = useGetAccount();
const tx = new Transaction({ nonce: BigInt(nonce) });WRONG: Insufficient gas limit
错误做法:Gas限制不足
typescript
// WRONG - 50000 gas is only for simple EGLD transfers
const tx = new Transaction({
gasLimit: BigInt(50000),
data: Buffer.from('ESDTTransfer@...') // Token transfer needs more gas!
});typescript
// 错误 - 50000 gas仅适用于简单EGLD转账
const tx = new Transaction({
gasLimit: BigInt(50000),
data: Buffer.from('ESDTTransfer@...') // 代币转账需要更多gas!
});CORRECT: Appropriate gas limits
正确做法:根据交易类型设置合适的Gas限制
typescript
// CORRECT gas limits by operation type:
// Simple EGLD transfer: 50,000
// ESDT token transfer: 500,000
// NFT transfer: 1,000,000
// Smart contract call: 5,000,000 - 60,000,000 (depends on complexity)
const tx = new Transaction({
gasLimit: BigInt(500000) // For token transfer
});typescript
// 不同操作类型的正确Gas限制:
// 简单EGLD转账: 50,000
// ESDT代币转账: 500,000
// NFT转账: 1,000,000
// 智能合约调用: 5,000,000 - 60,000,000(取决于复杂度)
const tx = new Transaction({
gasLimit: BigInt(500000) // 适用于代币转账
});WRONG: Rendering before initApp completes
错误做法:在initApp完成前渲染应用
typescript
// WRONG - SDK not initialized
createRoot(document.getElementById('root')!).render(<App />);
initApp(config); // Too late!typescript
// 错误 - SDK尚未初始化
createRoot(document.getElementById('root')!).render(<App />);
initApp(config); // 太晚了!CORRECT: Wait for initApp
正确做法:等待initApp完成
typescript
// CORRECT - SDK ready before render
initApp(config).then(() => {
createRoot(document.getElementById('root')!).render(<App />);
});typescript
// 正确 - SDK就绪后再渲染
initApp(config).then(() => {
createRoot(document.getElementById('root')!).render(<App />);
});API Endpoints Reference
API端点参考
| Network | API URL |
|---|---|
| Devnet | |
| Testnet | |
| Mainnet | |
| 网络 | API地址 |
|---|---|
| Devnet | |
| Testnet | |
| Mainnet | |
SDK Documentation Links
SDK文档链接
- SDK-dApp Documentation: https://github.com/multiversx/mx-sdk-dapp
- Template dApp (Reference Implementation): https://github.com/multiversx/mx-template-dapp
- SDK-Core Documentation: https://github.com/multiversx/mx-sdk-js-core
- MultiversX Docs: https://docs.multiversx.com
- SDK-dApp文档: https://github.com/multiversx/mx-sdk-dapp
- 模板dApp(参考实现): https://github.com/multiversx/mx-template-dapp
- SDK-Core文档: https://github.com/multiversx/mx-sdk-js-core
- MultiversX官方文档: https://docs.multiversx.com
Verification Checklist
验证清单
Before completion, verify:
- called in main.tsx BEFORE
initApp()createRoot().render() - Environment set correctly (devnet/testnet/mainnet)
- Wallet connection works with UnlockPanelManager
- correctly detects auth state
useGetIsLoggedIn() - Protected routes redirect unauthenticated users
- called before creating transactions
refreshAccount() - Gas limits appropriate for transaction type
- Chain ID from , not hardcoded
useGetNetworkConfig() - Transaction tracking shows toast notifications
- Smart contract ABI loaded correctly (if using contracts)
完成前请验证以下内容:
- 在main.tsx中,在
initApp()之前调用createRoot().render() - 环境设置正确(devnet/testnet/mainnet)
- 借助UnlockPanelManager实现的钱包连接功能正常
- 能正确检测认证状态
useGetIsLoggedIn() - 受保护路由会重定向未认证用户
- 创建交易前调用了
refreshAccount() - Gas限制与交易类型匹配
- 链ID来自,而非硬编码
useGetNetworkConfig() - 交易追踪会显示吐司通知
- 智能合约ABI加载正确(如果使用了合约)