Avail Nexus SDK 教程

本文是关于 Avail Nexus SDK 的系列教程的第一部分,主要介绍了如何使用 Nexus SDK 构建一个 Web3 应用程序,该程序可以跨多个区块链网络显示统一的token余额。

今天浏览 Web3 网络可能会让用户感觉像是在成为区块链专家后才能使用应用!他们需要在不同的链上管理 gas,手动桥接资产,并且不断切换网络。如果你的用户可以在你的应用程序内与多个区块链网络进行交互呢?

Avail Nexus SDK 让这一切成为现实。可以把它想象成互联网,你不会考虑哪个服务器托管网站,你只是无缝地浏览不同的网站和页面。Nexus SDK 对区块链也做了同样的事情,在后台处理所有复杂的多链操作。

在第 1 部分中,我们将建立基础,一个干净的 Web3 应用程序,可以跨多个链显示统一的 token 余额。这可能看起来很简单,但对于你的用户来说,这是一个重要的基础,他们可以在一个地方看到他们所有的资产,而不用管它们在哪个区块链上。


教程系列概览

这是我们全面的 Nexus SDK 教程的 第 1 部分,共 4 部分

✅ 第 1 部分:入门 (你在这里)

  • 从头开始设置 Nexus SDK
  • 构建统一的余额显示
  • 处理钱包连接和基本错误

🔜 第 2 部分:跨链操作 (即将推出)

  • 实现跨链桥接
  • 在网络之间无缝移动资产
  • 处理 token 转移确认和状态

🔜 第 3 部分:直接转移 (即将推出)

  • 直接跨链发送 token
  • 构建跨链支付流程
  • 高级交易处理

🔜 第 4 部分:生产就绪 (最后一部分)

  • 充满信心地部署到主网

  • 高级错误监控和分析

  • 性能优化和扩展

    • *

💡 只想获得代码?

你可以跳过教程并运行 完整的第 1 部分应用程序


我们在第 1 部分中构建的内容

我们将创建一个 统一余额视图,展示 Avail Nexus 的核心能力。

你将学到什么:

  • 如何从头开始设置 Nexus SDK
  • 构建跨多个链的统一余额显示
  • 实现钱包连接流程
  • 优雅地处理基本错误场景

你将构建什么:

  • 一个干净、响应式的 Web3 应用程序

  • 跨链的实时统一余额跟踪

  • 专业的钱包连接界面

  • 第 2-4 部分的坚实基础

    • *

前提条件

在我们开始之前,请确保你拥有:

  • Node.js (v16 或更高版本) - 在此下载

  • 一个包管理器 (npm, yarn, 或 pnpm)

  • React/Next.js 的基本知识 - hooks,组件,状态管理

  • 一个钱包扩展,例如 MetaMask,已安装并设置好

  • 用于测试的一些测试网 token (我们将向你展示如何获得它们)

    • *

重要注意事项

在我们深入研究代码之前,有几个关于 Nexus SDK 的关键事项需要理解。

仅限浏览器的环境

Nexus SDK 专门为浏览器环境设计,不能在服务器端上下文中使用。

这意味着:

  • 所有 SDK 操作都必须在客户端进行
  • 在 Next.js App Router 中使用 'use client' 指令
  • 在 API 路由或服务器组件中没有 SDK 调用

钱包 Provider 要求

SDK 需要一个注入的钱包 provider 才能运行:

  • MetaMask、WalletConnect 或类似的钱包扩展
  • 钱包必须已解锁并连接
  • 用户需要批准初始连接

初始化至关重要

必须在调用任何其他方法之前正确初始化 SDK:

  • 在初始化之前等待钱包连接

  • 优雅地处理初始化错误

  • 在设置过程中提供清晰的反馈

    • *

步骤 1:创建一个新项目

让我们从一个新的 Next.js 项目开始,我们将在整个教程系列中对其进行增强。

npx create-next-app@latest nexus-sdk-tutorial
cd nexus-sdk-tutorial

当出现提示时,选择这些选项以获得最佳开发体验:

✔ Would you like to use TypeScript? › Yes
✔ Would you like to use ESLint? › Yes
✔ Would you like to use Tailwind CSS? › Yes (optional, for styling)
✔ Would you like to use `src/` directory? › No
✔ Would you like to use App Router? › Yes
✔ Would you like to customize the default import alias? › No

为什么选择这些?

  • TypeScript: 提供更好的开发体验并尽早发现错误

  • ESLint: 帮助维护代码质量和一致性

  • Tailwind CSS: 使样式设置快速且一致 (可选但推荐)

  • App Router: 具有更好性能的现代 Next.js 路由系统

    • *

步骤 2:安装依赖项

安装 Nexus SDK 和所需的钱包连接库:

npm install @avail-project/nexus connectkit wagmi viem @tanstack/react-query lucide-react

如果遇到与 React 19 的依赖冲突:

npm install @avail-project/nexus connectkit wagmi viem @tanstack/react-query lucide-react --legacy-peer-deps

理解依赖项

  • @avail-project/nexus: 用于链抽象的核心 Nexus SDK

  • connectkit: 美观、用户友好的钱包连接 UI

  • wagmi: 用于 Ethereum 的 React hooks,提供钱包连接逻辑

  • viem: Ethereum 的 TypeScript 接口,是 ethers.js 的现代替代品

  • @tanstack/react-query: wagmi 所需的状态管理库,用于缓存和数据获取

    • *

步骤 3:设置 ConnectKit Provider

ConnectKit 提供无缝的钱包连接体验,与 Nexus SDK 配合良好。

创建 components/Web3Provider.tsx

'use client';

import { WagmiProvider, createConfig, http } from 'wagmi';
import { sepolia, baseSepolia, polygonAmoy, arbitrumSepolia, optimismSepolia } from 'wagmi/chains';
import { ConnectKitProvider, getDefaultConfig } from 'connectkit';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactNode, useState } from 'react';

// Configure wagmi with ConnectKit - testnets only for development
// 使用 ConnectKit 配置 wagmi - 仅测试网用于开发
const config = createConfig(
  getDefaultConfig({
    // Your dApp info
    // 你的 dApp 信息
    appName: 'Nexus SDK Tutorial - Part 1',
    appDescription: 'Learning chain abstraction with unified balance viewing',
    appUrl: 'https://localhost:3000',
    appIcon: '/avail-logo.svg', // Add Avail logo to your public folder
                                 // 将 Avail 徽标添加到你的公共文件夹

    // WalletConnect Project ID (get from https://cloud.walletconnect.com)
    // WalletConnect 项目 ID (从 https://cloud.walletconnect.com 获取)
    walletConnectProjectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID || 'your-wallet-connect-project-id',

    // Supported chains - testnets only for safe development
    // 支持的链 - 仅测试网用于安全开发
    chains: [\
      sepolia,        // Ethereum testnet
                     // Ethereum 测试网
      baseSepolia,    // Base testnet
                     // Base 测试网
      polygonAmoy,    // Polygon testnet
                      // Polygon 测试网
      arbitrumSepolia, // Arbitrum testnet
                       // Arbitrum 测试网
      optimismSepolia  // Optimism testnet
                       // Optimism 测试网
    ],
    transports: {
      [sepolia.id]: http(),
      [baseSepolia.id]: http(),
      [polygonAmoy.id]: http(),
      [arbitrumSepolia.id]: http(),
      [optimismSepolia.id]: http(),
    },
  })
);

interface Web3ProviderProps {
  children: ReactNode;
}

export function Web3Provider({ children }: Web3ProviderProps) {
  // Create a client for TanStack Query (required by wagmi)
  // 为 TanStack Query 创建一个客户端 (wagmi 需要)
  const [queryClient] = useState(() => new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 1000 * 60 * 5, // 5 minutes
                                  // 5 分钟
        retry: false,
      },
    },
  }));

  return (
    <QueryClientProvider client={queryClient}>
      <WagmiProvider config={config}>
        <ConnectKitProvider
          theme="auto"
          mode="light"
          customTheme={{
            "--ck-border-radius": "8px",
            "--ck-primary-button-background": "#2563eb",
            "--ck-primary-button-hover-background": "#1d4ed8",
          }}
          options={{
            enforceSupportedChains: false,
            walletConnectName: "Nexus Tutorial",
          }}
        >
          {children}
        </ConnectKitProvider>
      </WagmiProvider>
    </QueryClientProvider>
  );
}

配置说明

WalletConnect Project ID: 从 WalletConnect Cloud 免费获取。 这是移动钱包连接所必需的。

支持的测试网链: 本教程支持以下测试网网络:

  • Sepolia (11155111) - Ethereum 测试网
  • Base Sepolia (84532) - Base 测试网
  • Polygon Amoy (80002) - Polygon 测试网
  • Arbitrum Sepolia (421614) - Arbitrum 测试网
  • Optimism Sepolia (11155420) - Optimism 测试网

传输配置: 使用默认的 RPC 端点。 在第 4 部分中,我们将升级到专用的 RPC provider 以进行生产。


步骤 4:创建 Nexus Provider

这就是奇迹发生的地方!我们将创建一个 provider,用于初始化和管理 Nexus SDK。

创建 components/NexusProvider.tsx

'use client';

import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { useAccount } from 'wagmi';

// Global window extension for wallet provider
// 钱包 provider 的全局窗口扩展
declare global {
  interface Window {
    ethereum?: any;
  }
}

interface NexusContextType {
  sdk: any; // Replace with actual SDK type when available
               // 可用时替换为实际的 SDK 类型
  isInitialized: boolean;
  balances: any[];
  isLoading: boolean;
  error: string | null;
  refreshBalances: () => Promise<void>;
}

const NexusContext = createContext<NexusContextType | undefined>(undefined);

interface NexusProviderProps {
  children: ReactNode;
}

export function NexusProvider({ children }: NexusProviderProps) {
  const { isConnected, address } = useAccount();
  const [sdk, setSdk] = useState<any>(null);
  const [isInitialized, setIsInitialized] = useState(false);
  const [balances, setBalances] = useState<any[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // Initialize SDK when wallet connects
  // 当钱包连接时初始化 SDK
  useEffect(() => {
    if (isConnected && window.ethereum && !isInitialized && !isLoading) {
      initializeSDK();
    }
  }, [isConnected, isInitialized, isLoading]);

  // Fetch balances after SDK is initialized
  // 在 SDK 初始化后获取余额
  useEffect(() => {
    if (isInitialized && sdk) {
      fetchBalances(sdk);
    }
  }, [isInitialized, sdk]);

  const initializeSDK = async () => {
    try {
      setIsLoading(true);
      setError(null);

      // Dynamic import to ensure client-side only loading
      // 动态导入以确保仅在客户端加载
      const { NexusSDK } = await import('@avail-project/nexus');
      const nexusSDK = new NexusSDK({ network: 'testnet' });

      // Initialize with the wallet provider
      // 使用钱包 provider 初始化
      await nexusSDK.initialize(window.ethereum);

      // Set up allowance hook for token approvals
      // 设置 token 批准的 allowance hook
      nexusSDK.setOnAllowanceHook(async ({ allow, sources }: { allow: (allowances: string[]) => void; sources: any[] }) => {
        console.log('Allowance required for sources:', sources);
        // 允许需要源:, sources

        // For Part 1, we'll auto-approve with minimum allowances
        // 对于第 1 部分,我们将使用最小 allowance 自动批准
        // In Part 2, we'll build proper approval modals
        // 在第 2 部分中,我们将构建适当的批准弹窗
        const allowances = sources.map(() => 'min');
        allow(allowances);
      });

      // Set up intent hook for transaction previews
      // 设置交易预览的 intent hook
      nexusSDK.setOnIntentHook(({ intent, allow }: { intent: any; allow: () => void }) => {
        console.log('Transaction intent:', intent);
        // 交易意图:, intent

        // For Part 1, we'll auto-approve
        // 对于第 1 部分,我们将自动批准
        // In Part 3, we'll build transaction preview modals
        // 在第 3 部分中,我们将构建交易预览弹窗
        allow();
      });

      setSdk(nexusSDK);
      setIsInitialized(true);

      // Fetch initial balances
      // 获取初始余额
      await fetchBalances(nexusSDK);

    } catch (error) {
      console.error('Failed to initialize Nexus SDK:', error);
      // 无法初始化 Nexus SDK:, error
      setError(error instanceof Error ? error.message : 'Failed to initialize SDK');
      // 无法初始化 SDK
    } finally {
      setIsLoading(false);
    }
  };

  const fetchBalances = async (sdkInstance = sdk) => {
    if (!sdkInstance || !isInitialized) return;

    try {
      setIsLoading(true);
      setError(null);

      const unifiedBalances = await sdkInstance.getUnifiedBalances();
      setBalances(unifiedBalances);
      console.log('Unified balances fetched:', unifiedBalances);
      // 统一的余额已获取:, unifiedBalances

    } catch (error) {
      console.error('Failed to fetch balances:', error);
      // 无法获取余额:, error
      setError(error instanceof Error ? error.message : 'Failed to fetch balances');
      // 无法获取余额
    } finally {
      setIsLoading(false);
    }
  };

  const refreshBalances = async () => {
    await fetchBalances();
  };

  // Reset state when wallet disconnects
  // 当钱包断开连接时重置状态
  useEffect(() => {
    if (!isConnected) {
      setSdk(null);
      setIsInitialized(false);
      setBalances([]);
      setError(null);
    }
  }, [isConnected]);

  return (
    <NexusContext.Provider
      value={{
        sdk,
        isInitialized,
        balances,
        isLoading,
        error,
        refreshBalances
      }}
    >
      {children}
    </NexusContext.Provider>
  );
}

export function useNexus() {
  const context = useContext(NexusContext);
  if (context === undefined) {
    throw new Error('useNexus must be used within a NexusProvider');
    // useNexus 必须在 NexusProvider 中使用
  }
  return context;
}

主要功能说明

动态导入: 我们使用动态导入来确保 SDK 仅在客户端加载,从而防止服务器端渲染问题。

自动初始化: 当钱包连接时,SDK 会自动初始化,从而提供无缝的用户体验。

错误处理: 全面的错误处理,具有用户友好的错误消息和适当的清理。

Hook 架构: 该 SDK 使用 hooks 进行 allowance 和 intent。 我们将在第 2 部分和第 3 部分中对此进行更多探讨。


步骤 5:创建钱包连接组件

一个干净、用户友好的钱包连接界面。

创建 components/WalletConnection.tsx

'use client';

import { ConnectKitButton } from 'connectkit';
import { useAccount } from 'wagmi';
import { Wallet, CheckCircle, AlertCircle } from 'lucide-react';

export function WalletConnection() {
  const { isConnected } = useAccount();

  return (
    <ConnectKitButton.Custom>
      {({ isConnected, show, truncatedAddress, ensName, chain }) => {
        return (
          <button
            onClick={show}
            className={`
              flex items-center space-x-2 px-4 py-2 rounded-lg font-medium transition-all duration-200
              ${isConnected
                ? 'bg-green-50 text-green-700 border border-green-200 hover:bg-green-100'
                : 'bg-blue-600 text-white hover:bg-blue-700 shadow-lg hover:shadow-xl'
              }
            `}
          >
            {isConnected ? (
              <CheckCircle className="w-4 h-4" />
            ) : (
              <Wallet className="w-4 h-4" />
            )}
            <span>
              {isConnected ? (ensName ?? truncatedAddress) : 'Connect Wallet'}
              // {isConnected ? (ensName ?? truncatedAddress) : '连接钱包'}
            </span>
            {chain && (
              <span className="text-xs bg-white/20 px-2 py-1 rounded">
                {chain.name}
              </span>
            )}
          </button>
        );
      }}
    </ConnectKitButton.Custom>
  );
}

// Alternative: Simple default ConnectKit button for minimal setup
// 替代方案:用于最小化设置的简单默认 ConnectKit 按钮
export function SimpleWalletConnection() {
  return <ConnectKitButton />;
}

设计选择

视觉反馈: 该按钮根据连接状态更改外观,从而提供即时视觉反馈。

链显示: 显示当前连接的链,以提高用户意识。

响应式设计: 在桌面设备和移动设备上都能很好地工作。


步骤 6:创建统一余额显示

该组件通过显示所有链上的统一余额来展示 Nexus SDK 的强大功能。

创建 components/UnifiedBalances.tsx

'use client';

import { useNexus } from './NexusProvider';
import { RefreshCw, Coins, TrendingUp, AlertCircle, ArrowRight } from 'lucide-react';

export function UnifiedBalances() {
  const { balances, isLoading, sdk, isInitialized, error, refreshBalances } = useNexus();

  if (!isInitialized) {
    return (
      <div className="text-center py-8">
        <div className="inline-flex items-center space-x-2 text-slate-500">
          <Coins className="w-5 h-5" />
          <span>Connect your wallet to view unified balances</span>
          // <span>连接你的钱包以查看统一的余额</span>
        </div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="text-center py-8">
        <div className="inline-flex items-center space-x-2 text-red-500 bg-red-50 px-4 py-2 rounded-lg">
          <AlertCircle className="w-5 h-5" />
          <span>Error: {error}</span>
          // <span>错误:{error}</span>
        </div>
        <button
          onClick={refreshBalances}
          className="mt-4 text-blue-600 hover:text-blue-700 underline"
        >
          Try Again
          // 再次尝试
        </button>
      </div>
    );
  }

  return (
    <div className="space-y-6">
      {/* Header */}
      {/* 页眉 */}
      <div className="flex justify-between items-center">
        <div>
          <h2 className="text-2xl font-bold text-slate-900">Unified Balances</h2>
          {/* 统一余额 */}
          <p className="text-slate-600">Your assets across all connected chains</p>
          {/* 你在所有已连接链上的资产 */}
        </div>
        <button
          onClick={refreshBalances}
          disabled={isLoading}
          className="flex items-center space-x-2 bg-slate-100 hover:bg-slate-200 disabled:opacity-50 text-slate-700 font-medium py-2 px-4 rounded-lg transition-colors"
        >
          <RefreshCw className={`w-4 h-4 ${isLoading ? 'animate-spin' : ''}`} />
          <span>{isLoading ? 'Loading...' : 'Refresh'}</span>
          {/* <span>{isLoading ? '正在加载...' : '刷新'}</span> */}
        </button>
      </div>

      {/* Balance Cards */}
      {/* 余额卡片 */}
      {balances.length === 0 ? (
        <div className="text-center py-12">
          <div className="inline-flex items-center space-x-2 text-slate-500">
            <TrendingUp className="w-5 h-5" />
            <span>No balances found across connected chains</span>
            {/* <span>在已连接的链上未找到余额</span> */}
          </div>
          <p className="text-sm text-slate-400 mt-2">
            Make sure you have assets on supported networks
            {/* 确保你在受支持的网络上拥有资产 */}
          </p>
        </div>
      ) : (
        <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
          {balances.map((balance, index) => (
            <div
              key={index}
              className="p-6 bg-white rounded-xl border border-slate-200 shadow-sm hover:shadow-md transition-shadow"
            >
              <div className="flex justify-between items-start mb-4">
                <div className="flex items-center gap-2">
                  {balance.icon && (
                    <img src={balance.icon} alt={balance.symbol} className="w-6 h-6 rounded-full" />
                  )}
                  <h3 className="font-semibold text-lg text-slate-900">
                    {balance.symbol}
                  </h3>
                </div>
                <div className="text-right">
                  <p className="text-xl font-mono font-bold text-slate-900">
                    {parseFloat(balance.balance).toFixed(4)}
                  </p>
                  {balance.balanceInFiat > 0 && (
                    <p className="text-sm text-slate-500">
                      ${balance.balanceInFiat.toFixed(2)}
                    </p>
                  )}
                </div>
              </div>
              {/* Per-chain breakdown */}
              {/* 每个链的细分 */}
              {balance.breakdown && balance.breakdown.length > 0 && (
                <div className="mt-2 space-y-1">
                  {balance.breakdown.map((item: any, idx: number) => (
                    <div
                      key={idx}
                      className={`text-sm flex justify-between ${item.balance === '0' ? 'text-gray-400' : 'text-gray-600'}`}
                    >
                      <span className="flex items-center gap-1">
                        {item.chain.name}
                      </span>
                      <span className="font-mono">{item.balance}</span>
                    </div>
                  ))}
                </div>
              )}
            </div>
          ))}
        </div>
      )}

      {/* Summary Stats */}
      {/* 摘要统计信息 */}
      {balances.length > 0 && (
        <div className="bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl p-6 border border-blue-100">
          <h3 className="font-semibold text-slate-900 mb-3">Portfolio Summary</h3>
          <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
            <div>
              <p className="text-sm text-slate-500">Total Assets</p>
              {/* 总资产 */}
              <p className="text-lg font-semibold text-slate-900">{balances.length}</p>
            </div>
            <div>
              <p className="text-sm text-slate-500">Chains</p>
              {/* 链 */}
              <p className="text-lg font-semibold text-slate-900">
                {new Set(balances.map(b => b.chainId)).size}
              </p>
            </div>
            <div>
              <p className="text-sm text-slate-500">Last Updated</p>
              {/* 上次更新*/}
              <p className="text-sm text-slate-700">Just now</p>
              {/* 刚刚 */}
            </div>
            <div>
              <p className="text-sm text-slate-500">Status</p>
              {/* 状态 */}
              <p className="text-sm text-green-600 font-medium">Active</p>
              {/* 活动 */}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

组件功能

统一显示: 在一个连贯的界面中显示来自所有链的余额,这是 Nexus 的核心价值!

实时更新: 刷新按钮允许用户获取最新的余额信息。

错误处理: 优雅地处理和显示错误状态以及恢复选项。

投资组合摘要: 提供有关用户多链投资组合的有用见解。


步骤 7:更新根布局

将所有 provider 和组件连接在一起。

更新 app/layout.tsx

import { Web3Provider } from '@/components/Web3Provider';
import { NexusProvider } from '@/components/NexusProvider';
import { Inter } from 'next/font/google';
import './globals.css';

const inter = Inter({ subsets: ['latin'] });

export const metadata = {
  title: 'Nexus SDK Tutorial - Part 1: Getting Started',
  description: 'Learn chain abstraction by building unified balance viewing with the Avail Nexus SDK',
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <Web3Provider>
          <NexusProvider>
            {children}
          </NexusProvider>
        </Web3Provider>
      </body>
    </html>
  );
}

步骤 8:创建主应用程序页面

将所有内容整合到一个有凝聚力的用户界面中。

更新 app/page.tsx

'use client';

import { WalletConnection } from '@/components/WalletConnection';
import { UnifiedBalances } from '@/components/UnifiedBalances';
import { useAccount } from 'wagmi';
import { useNexus } from '@/components/NexusProvider';
import { Globe, Zap, Shield, ArrowRight } from 'lucide-react';

export default function Home() {
  const { isConnected } = useAccount();
  const { isInitialized, isLoading } = useNexus();

  return (
    <main className="min-h-screen bg-gradient-to-br from-slate-50 to-blue-50">
      <div className="container mx-auto px-4 py-8">

        {/* Header */}
        {/* 页眉 */}
        <div className="text-center mb-12">
          <div className="inline-flex items-center space-x-2 mb-4">
            <Globe className="w-8 h-8 text-blue-600" />
            <h1 className="text-4xl font-bold text-slate-900">
              Nexus SDK Tutorial
            </h1>
          </div>
          <div className="mb-4">
            <span className="bg-blue-100 text-blue-800 text-sm font-medium px-3 py-1 rounded-full">
              Part 1: Getting Started
            </span>
          </div>
          <p className="text-xl text-slate-600 max-w-2xl mx-auto">
            Experience unified Web3 interactions by viewing your balances across
            multiple blockchains in one place
          </p>
          {/* 通过在一个地方查看你在多个区块链上的余额来体验统一的 Web3 交互 */}
        </div>

        {/* Wallet Connection */}
        {/* 钱包连接 */}
        <div className="flex justify-center mb-8">
          <WalletConnection />
        </div>

        {/* Main Content */}
        {/* 主要内容 */}
        {isConnected ? (
          <div className="max-w-4xl mx-auto">
            <UnifiedBalances />
          </div>
        ) : (
          <div className="text-center py-12">
            <div className="max-w-md mx-auto">
              <h2 className="text-2xl font-bold text-slate-900 mb-4">
                Ready to Experience Chain Abstraction?
              </h2>
              {/* 准备好体验链抽象了吗? */}
              <p className="text-slate-600 mb-8">
                Connect your wallet to see how the Nexus SDK unifies your multi-chain portfolio
              </p>
              {/* 连接你的钱包以了解 Nexus SDK 如何统一你的多链投资组合 */}
              <div className="bg-white/60 backdrop-blur-sm rounded-xl p-8 border border-slate-200">
                <p className="text-sm text-slate-500">
                  This demo will show you how the Nexus SDK aggregates balances
                  across multiple chains in real-time
                </p>
                {/* 此演示将向你展示 Nexus SDK 如何实时聚合多个链上的余额 */}
              </div>
            </div>
          </div>
        )}

        {/* Loading State */}
        {/* 加载状态 */}
        {isConnected && isLoading && !isInitialized && (
          <div className="fixed inset-0 bg-black/20 backdrop-blur-sm flex items-center justify-center z-50">
            <div className="bg-white rounded-xl p-6 shadow-xl">
              <div className="flex items-center space-x-3">
                <div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
                <span className="text-slate-700">Initializing Nexus SDK...</span>
                {/* 正在初始化 Nexus SDK... */}
              </div>
            </div>
          </div>
        )}
      </div>
    </main>
  );
}

步骤 9:环境配置

设置你的环境变量以实现正确的钱包连接。

创建 .env.local

## WalletConnect Project ID (required for mobile wallet support)
## 从 https://cloud.walletconnect.com 免费获取
## Get this free from https://cloud.walletconnect.com
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_id

## Optional: App metadata
## 可选:应用程序元数据
NEXT_PUBLIC_APP_NAME="Nexus SDK Tutorial - Part 1"
NEXT_PUBLIC_APP_DESCRIPTION="Learning chain abstraction with unified balance viewing"

获取你的 WalletConnect 项目 ID

  1. 访问 WalletConnect Cloud
  2. 注册一个免费帐户
  3. 创建一个新项目
  4. 复制你的项目 ID
  5. 将其粘贴到你的 .env.local 文件中

注意: 这是移动钱包连接所必需的,但即使没有它,该应用程序也可以与 MetaMask 等浏览器钱包一起使用。


步骤 10:测试你的集成

现在让我们测试所有内容,以确保其完美运行。

1. 启动开发服务器

npm run dev

2. 测试清单

访问 http://localhost:3000 并验证:

✅ 基本功能:

  • [ ] 应用加载没有错误
  • [ ] 第 1 部分徽章显示正确
  • [ ] 出现钱包连接按钮
  • [ ] 可以成功连接 MetaMask
  • [ ] 连接状态正确更新

✅ Nexus SDK 集成:

  • [ ] SDK 在钱包连接后初始化
  • [ ] 加载状态显示正确
  • [ ] 出现统一余额 (如果你有资产)
  • [ ] 刷新功能有效

✅ 错误处理:

  • [ ] 当未找到余额时,可以正常处理
  • [ ] 错误消息显示清晰
  • [ ] 恢复选项有效 (再次尝试按钮)

✅ 用户体验:

  • [ ] 移动设备上的响应式设计

  • [ ] 平滑的过渡和动画

  • [ ] 对所有操作的清晰视觉反馈

    • *

步骤 11:了解你构建的内容

恭喜!你刚刚构建了一些很棒的东西。让我们了解一下是什么让这个演示与众不同:

统一余额的魔力

在以前,用户必须:

  1. 手动在他们的钱包中切换网络
  2. 分别检查每条链 以获取他们的资产
  3. 记住哪些资产在哪条链上
  4. 管理不同网络的多个 gas token

通过你的 Nexus SDK 集成,用户现在可以:

  1. 连接一次 并查看所有内容
  2. 在一个界面中查看所有统一的资产
  3. 获取所有链上的实时更新
  4. 体验无缝的多链 交互

技术成就

智能 SDK 管理:你的应用程序在钱包连接时自动初始化 SDK,并在断开连接时清理它。

错误恢复:全面的错误处理可确保用户在出现问题时始终有前进的道路。

性能优化:加载状态和适当的状态管理创造了流畅的用户体验。

面向未来:你构建的基础将支持第 2-4 部分中的高级功能。


问题排查

常见问题和快速修复

🔧 SDK 无法初始化

  • 确保 MetaMask 已解锁并连接
  • 检查浏览器控制台以获取特定错误消息
  • 尝试在钱包连接后刷新页面
  • 验证你是否未在服务器端上下文中运行

🔧 未显示任何余额

  • 确保你在受支持的网络 (Ethereum、Polygon、Arbitrum、Base、Optimism) 上#### ✅ 你构建的内容:

  • 统一余额视图 - 在一个地方查看所有链上的资产

  • 智能钱包集成 - 与视觉反馈的无缝连接

  • 实时更新 - 具有刷新功能的新鲜余额数据

  • 错误恢复 - 优雅地处理网络问题和极端情况

  • 专业的 UI - 具有加载状态的干净、响应式设计

✅ 你学到的内容:

  • 如何从头开始设置 Nexus SDK

  • 使用 Avail Nexus 构建统一的 Web3 体验

  • 正确的错误处理和用户反馈模式

  • 创建可用于生产的钱包连接流程

    • *

准备好进行第 2 部分了吗?

第 2 部分:跨链操作中,我们将通过添加以下内容来解锁 Nexus SDK 的真正力量:

跨链转移

  • 一键在链之间移动资产
  • 实时 Token 转移状态跟踪
  • 自动费用计算和优化

智能交易流程

  • 基于意图的交易系统
  • 自动路由优化
  • 内置滑点保护

增强的用户体验

  • 交易预览模态框
  • 跨多个链的进度跟踪
  • 高级错误恢复模式

为第 2 部分做好准备:

  • 保留你的第 1 部分项目 - 我们将在其之上构建

  • 确保你在不同的链上有一些测试网 Token

  • 从水龙头获取测试网 ETH 用于测试

    • *

社区 & 资源

分享你的进度

构建了第 1 部分? 我们很乐意看到它!

  • Discord - 加入我们的开发者社区
  • Twitter - 与 Avail Nexus 分享你的进度
  • GitHub - 探索 Avail

继续学习

接下来:第 2 部分,我们将添加改变游戏规则的跨链桥接功能,这将使 Nexus SDK 真正具有革命性!

教程 Avail Nexus 开发者 传递区块

  • 原文链接: blog.availproject.org/av...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Avail Project
Avail Project
Build with Avail DA, the validity proven data availability layer unifying Web3