如何在 Next.js 中集成 MetaMask SDK

了解如何在 Next.js 中集成 MetaMask 的「连接钱包」按钮。本教程涵盖环境配置、MetaMask SDK 使用方法,以及使用 ShadcnUI 创建用户界面等内容……

6 分钟阅读
如何在 Next.js 中集成 MetaMask SDK

在 Web3 中,钱包是指一种数字应用程序,允许用户与区块链网络进行交互。钱包充当用户与去中心化区块链世界之间的桥梁。与使用电子邮件和密码组合或 OAuth 服务进行身份验证的传统 Web 应用程序不同,Web3 应用程序基于钱包所有权及其关联私钥对用户进行身份验证。"连接钱包"按钮将启动此身份验证过程。

在本教程中,我们将在 Next.js 应用中实现一个"连接钱包"按钮,用于启动与 MetaMask 钱包的身份验证过程。

前提条件:

  • 熟悉 Next.js 和 React。

  • 已安装 Node.js 和 npm/yarn。

  • 已在浏览器中安装 MetaMask 扩展程序。

以上是本教程的一些前提条件,但最好的学习方式是直接上手,边做边学。

搭建 Next.js 项目:

要搭建 Next.js 项目,您需要在计算机上安装 Node.js。安装 Node.js 后,您可以打开终端并运行以下命令来创建新的 Next.js 应用程序:

npx create-next-app@latest

我们将看到以下提示:

npx create-next-app@latest
✔ What is your project named? … next-metamask-template
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
复制

此命令将使用指定的项目名称创建一个新文件夹,设置初始 Next.js 结构,并安装所有必要的依赖项。导航到您的文件夹。

cd next-metamask-template

然后运行命令 npm run dev,该命令将以开发模式启动 Next.js 应用程序,您可以在浏览器中访问 http://localhost:3000 来查看您的应用程序。

创建用户界面

我们将为应用添加一个组件库。接下来安装 ShadcnUI。

npx shadcn-ui@latest init

我们将看到以下配置 components.json 的提示:

Would you like to use TypeScript (recommended)? no / yes
Which style would you like to use? › Default
Which color would you like to use as base color? › Slate
Where is your global CSS file? › › app/globals.css
Do you want to use CSS variables for colors? › no / yes
Where is your tailwind.config.js located? › tailwind.config.js
Configure the import alias for components: › @/components
Configure the import alias for utils: › @/lib/utils
Are you using React Server Components? › no / yes
复制

在本教程中,我们将使用 ShadcnUI 组件库中的两个组件:Button 和 Popover。

我们还将使用 @metamask/sdk-react,您可以从 MetaMask 文档中了解更多相关信息。

安装 SDK:

npm i @metamask/sdk-react

我们将创建一个名为 NavBar.tsx 的新组件,并添加"连接钱包"按钮和 SDK。我们的组件初始应如下所示:

"use client";

import Link from "next/link";

import WalletIcon from "../public/icons/WalletIcon";

import { Button } from "./ui/button";

import { useSDK, MetaMaskProvider } from "@metamask/sdk-react";
import { formatAddress } from "../lib/utils";
import {
  Popover,
  PopoverTrigger,
  PopoverContent,
} from "@/components/ui/popover";

export const ConnectWalletButton = () => {
  const { sdk, connected, connecting, account } = useSDK();

  const connect = async () => {
    try {
      await sdk?.connect();
    } catch (err) {
      console.warn(`No accounts found`, err);
    }
  };

  const disconnect = () => {
    if (sdk) {
      sdk.terminate();
    }
  };

  return (
    <div className="relative">
      {connected ? (
        <Popover>
          <PopoverTrigger>
            <Button>{formatAddress(account)}</Button>
          </PopoverTrigger>
          <PopoverContent className="mt-2 w-44 bg-gray-100 border rounded-md shadow-lg right-0 z-10 top-10">
            
              Disconnect
            
          </PopoverContent>
        </Popover>
      ) : (
        <Button disabled={connecting} onClick={connect}>
          <WalletIcon className="mr-2 h-4 w-4" /> Connect Wallet
        </Button>
      )}
    </div>
  );
};
复制

让我们来看看我们做了什么:

1. useSDK hook:首先,我们关注的是 useSDK hook,它允许访问 MetaMask SDK 的功能。该 hook 提供了一个用于与 MetaMask 交互的 SDK 对象、一个用于验证钱包是否已连接的 connected 布尔值、一个用于检查连接状态的 connecting 布尔值,以及一个用于访问用户账户地址的 account。

2. 连接和断开连接函数:设置包含两个关键函数:connect 和 disconnect。connect 函数是一个异步操作,尝试使用 SDK 与 MetaMask 钱包建立连接。连接成功后,它将更新 useSDK hook 中的 connected 和 account 值。disconnect 函数则通过调用 sdk.terminate() 来断开钱包连接。

3. UI 元素:我们的实现包含两个 UI 元素——按钮和弹出框。组件的渲染会根据连接状态而变化。连接后,将显示一个 Popover 组件,展示格式化的账户地址和断开连接的选项。未连接时,则显示"连接钱包"按钮,该按钮在连接尝试进行中时将被禁用。

现在,让我们为 NavBar 添加代码:

export const NavBar = () => {
  const host =
    typeof window !== "undefined" ? window.location.host : "defaultHost";

  const sdkOptions = {
    logging: { developerMode: false },
    checkInstallationImmediately: false,
    dappMetadata: {
      name: "Next-Metamask-Boilerplate",
      url: host, // using the host constant defined above
    },
  };

  return (
    <nav className="flex items-center justify-between max-w-screen-xl px-6 mx-auto py-7 rounded-xl">
      <Link href="/" className="flex gap-1 px-6">
        <span className="hidden text-2xl font-bold sm:block">
          <span className="text-gray-900">Template</span>
        </span>
      </Link>
      <div className="flex gap-4 px-6">
        <MetaMaskProvider debug={false} sdkOptions={sdkOptions}>
          <ConnectWalletButton />
        </MetaMaskProvider>
      </div>
    </nav>
  );
};

export default NavBar;
复制

让我们来看看我们做了什么:

  1. Host 变量:该变量用于确定当前窗口的主机地址,以供 SDK 选项使用。它包含一个条件检查,用于验证是否在浏览器环境中运行。

  2. SDK 选项:这些是 MetaMask SDK 的配置选项,用于设置日志记录和安装检查的偏好,并包含有关 dapp 的元数据。

  3. NavBar 组件:NavBar 包含一个指向主页的链接,并集成了 ConnectWalletButton 组件。为了提供 MetaMask SDK 所需的上下文,它使用从 @metamask/sdk-react 导入的 MetaMaskProvider 组件对 ConnectWalletButton 进行了包装。

  • 钱包图标:为了获取钱包图标,我们使用了 SVGR,这是一个将 SVG 图像转换为 React 组件的工具。

为了防止完整的钱包地址在 NavBar 中占用过多空间,我们将在 lib 文件夹中的 utils.ts 文件中进行调整,以确保地址以更简洁的格式显示。

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}
复制

添加以下代码行:

export const formatBalance = (rawBalance: string) => {
  const balance = (parseInt(rawBalance) / 1000000000000000000).toFixed(2);
  return balance;
};

export const formatChainAsNum = (chainIdHex: string) => {
  const chainIdNum = parseInt(chainIdHex);
  return chainIdNum;
};

export const formatAddress = (addr: string | undefined) => {
  return `${addr?.substring(0, 8)}...`;
};
复制

上述函数——formatBalance、formatChainAsNum 和 formatAddress——旨在被导入并在整个 dApp 中使用,以统一格式化区块链数据并向用户展示。

在 NavBar 组件中,我们将按如下方式导入:

import { formatAddress } from "../lib/utils";

并在按钮中使用:

现在剩下的就是将 NavBar.tsx 导入到 layout.tsx 组件中。

运行开发服务器后,我们将看到对应用所做的更改。

总结

MetaMask SDK 在构建 DApp 方面真正便捷,堪称游戏规则的改变者。它使用户能够轻松连接到 MetaMask 浏览器扩展程序和 MetaMask 移动端。

这只是 MetaMask SDK 入门介绍,但我们还可以用它做更多事情,也可以在全栈应用程序中使用它。您可以在此处的代码仓库中找到本项目的代码。

我还推荐查阅以下资源:

祝开发愉快。🚀

AI 翻译。可能包含错误。请务必核实信息。

评价此翻译