Wagmi + ReactJS 示例:转账加密货币并铸造 NFT

本教程详细介绍了如何使用Next.js和Wagmi.sh构建一个Web3 Dapp,允许用户通过连接到加密货币钱包来转移资金和铸造NFT。教程包括从设置开发环境到实现转账和NFT铸造的完整步骤。

在本教程中,我们将学习如何构建一个连接到你的加密钱包的 Web3 Dapp(去中心化应用程序),使你能够转账和铸造NFT。我们将使用 Next.js,这是一个流行的 React 框架,以及 Wagmi.sh,这是一组 React Hooks,可以轻松将你的钱包集成到你的网站中。

以下是教程的大纲:

  • 入门
  • 第1部分:使用 React + Wagmi 转移加密货币
  • 第2部分:使用 React + Wagmi 铸造 NFT

作者

本文由 RareSkills 的研究实习生 Aymeric Taylor 共同撰写 (LinkedIn, Twitter)。

入门

为了简化起见,我们将使用“Polygon Mumbai”作为本教程的测试网络,它在 OpenSea 上也受支持。

将你的钱包连接到 Polygon Mumbai – MetaMask

通过 MetaMask → 设置 → 高级,确保你允许 显示测试网络

metamask test networks

接下来,点击右上角的网络选择,选择 添加网络

metamask add network

如果 Polygon Mumbai 尚不可选,则可以按照以下步骤操作。

选择 手动添加网络 并输入以下内容:

网络名称: Matic Mumbai

新的 RPC URL: https://rpc-mumbai.maticvigil.com/

链 ID: 80001

货币符号: MATIC

区块探测器 URL(可选) https://mumbai.polygonscan.com/

现在只需切换到 Polygon 网络即可。

Wagmi 示例 第1部分:使用 React + Wagmi 转移以太坊

第1步:设置带 node js 的网站

你需要安装 nodejs。

首先使用以下命令创建你的 Next.js 项目:

npx create-next-app@latest myapp

使用箭头和 Enter 键检查 TypescriptESLint 选项。它应该类似于:

create react app typescript

vscode 中打开你的项目并安装 wagmi.shuse-debounce package(稍后会涉及到)。

npm i wagmi ethers@^5
npm i use-debounce --save

Wagmi 基本上是一组 React Hooks,通过提供连接钱包和与合约交互等有用功能,简化了 以太坊开发,这些我们将在本教程中学习。有关更多内容,见 wagmi.sh

第2步:使用 configureChains 选择要连接的网络

前往 pages/_app.tsx 并添加以下代码。每个功能在注释部分都有说明。

import "@/styles/globals.css"; // CSS 现在并不重要
import type { AppProps } from "next/app";
import { WagmiConfig, configureChains, createClient, mainnet } from "wagmi";
import { publicProvider } from "wagmi/providers/public";
import { polygonMumbai } from "wagmi/chains";
import { CoinbaseWalletConnector } from 'wagmi/connectors/coinbaseWallet'
import { InjectedConnector } from 'wagmi/connectors/injected'
import { MetaMaskConnector } from 'wagmi/connectors/metaMask'
import { WalletConnectConnector } from 'wagmi/connectors/walletConnect'

// 配置你想要使用的链和提供程序, 
// 请记住,你可以传递任何 EVM 兼容链。
// 也建议你同时传递 alchemyProvider 和 infuraProvider。
const { chains, provider, webSocketProvider } = configureChains(
  [mainnet, polygonMumbai],
  [publicProvider()]
);

// 这会创建一个 wagmi 客户端实例并传入提供者和 webSocketProvider。

const client = createClient({
  autoConnect: false,
  provider,
  webSocketProvider,
  connectors: [ // connectors 用于连接你的钱包,默认为 InjectedConnector();
    new MetaMaskConnector({ chains }),
    new CoinbaseWalletConnector({
      chains,
      options: {
        appName: 'wagmi',
      },
    }),
    new WalletConnectConnector({
      chains,
      options: {
        projectId: '...',
      },
    }),
    new InjectedConnector({
      chains,
      options: {
        name: 'Injected',
        shimDisconnect: true,
      },
    }),
  ],
});

export default function App({ Component, pageProps }: AppProps) {
  return (
    // 用 WagmiConfig 组件包裹你的应用程序 
    // 并将 client 实例作为 prop 传递给它。
    <WagmiConfig client={client}>
      <Component {...pageProps} />
    </WagmiConfig>
  );
}

现在我们已经配置了网络,下一步是让用户选择要连接的钱包。如上所示,我们设置了 4 个钱包连接器。MetaMask、WalletConnect、Coinbase 和 Injected(这基本上是你的默认钱包)。

第3步:使用 useConnect 启用选择浏览器钱包

pages/index.tsx 中复制并粘贴以下代码。确保你添加 CSS,以使其看起来不错!

import { useAccount, useConnect } from "wagmi";
import { useEffect } from "react";
import styles from "@/styles/Home.module.css";

export default function Home() {
  const { connect, connectors } = useConnect();
  const { isConnected } = useAccount();

  useEffect(() => {
    console.log(
      `当前连接状态: ${isConnected ? "已连接" : "未连接"}`
    );
  }, [isConnected]);

  return (
    <>
      <p
        className={styles.status}
        style={{
          color: isConnected ? "green" : "red",
        }}
      >
        {" "}
        {isConnected !== undefined
          ? isConnected
            ? "已连接"
            : "未连接"
          : "加载中..."}
      </p>
      <div className={styles.maincontainer}>
        <div className={styles.container}>
          <div className={styles.buttonswrapper}>
            <div className={styles.buttonswrapperGrid}>
              {connectors.map((connector) => (
                <button
                  suppressHydrationWarning
                  key={connector.id}
                  onClick={() => connect({ connector })}
                  className={styles.button28}
                >
                  {connector.name}
                </button>
              ))}
            </div>
          </div>
        </div>

        {/* 发送资金 */}

        {/* 铸造 nft */}
      </div>
    </>
  );
}

第4步:添加 CSS 使其看起来不错

删除 styles/globals.cssstyles/Home.module.css 中的所有内容。

将以下 CSS 代码复制粘贴到 styles/globals.css 中。

body {
    height: 100vh;     
    background: rgb(11,3,48); /* 未支持渐变的浏览器 */
    background: linear-gradient(to bottom right,#0b0330, #5904a4);   
    font-family: 'Inter Medium', sans-serif;
}

将以下 CSS 代码复制到 styles/Home.module.css 中。

.status {
  text-align: left;
  margin: 0px;
  font-family: "Inter Medium", sans-serif;
}

.maincontainer {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  height: 60%;
  width: 60%;
}

.buttonswrapperGrid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  grid-gap: 1.5rem;
  justify-items: center;
  align-items: center;
  justify-content: center;
  margin-bottom: 40px;
  padding: 20px;
}

/* CSS */
.button28 {
  align-items: center;
  background-color: #da5fff;
  border: 2px solid #1d0321;
  border-radius: 8px;
  box-sizing: border-box;
  color: #111;
  cursor: pointer;
  display: flex;
  font-family: Inter, sans-serif;
  font-size: 16px;
  height: 55px;
  justify-content: center;
  line-height: 24px;
  max-width: 75%;
  padding: 0 25px;
  position: relative;
  text-align: center;
  text-decoration: none;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  width: 100%;
}

.button28:after {
  background-color: #210c20;
  border-radius: 8px;
  content: "";
  display: block;
  height: 48px;
  left: 0;
  width: 100%;
  position: absolute;
  top: -2px;
  transform: translate(8px, 14px);
  transition: transform 0.2s ease-out;
  z-index: -1;
}

.button28:hover:after {
  transform: translate(0, 0);
}

.button28:active {
  background-color: #ffdeda;
  outline: 0;
}

.button28:hover {
  outline: 0;
}

@media (min-width: 768px) {
  .button28 {
    padding: 0 40px;
  }
}

.myinput {
  background-color: rgb(253, 232, 255);
  border-radius: 15px;
  border: 2px solid #1d0321;
  padding: 20px;
  width: 44%;
  height: 15px;
}

.inputcontainer {
  display: flex;
  justify-content: space-between; /* 均匀间隔输入框 */
  align-items: center; /* 垂直对齐输入框 */
}

.buttoncontainer {
  display: flex;
  justify-content: center; /* 水平居中按钮 */
  align-items: center; /* 垂直居中按钮 */
  margin-top: 16px;
  margin-bottom: 16px;
}
.mintcontainer {
  display: flex;
  justify-content: center; /* 水平居中按钮 */
  align-items: center; /* 垂直居中按钮 */
  margin-top: 50px;
  margin-bottom: 16px;
}
/* CSS */
.button64 {
  align-items: center;
  background-image: linear-gradient(144deg, #af40ff, #280b36 50%, #e30eff);
  border: 0;
  border-radius: 8px;
  box-shadow: rgba(151, 65, 252, 0.2) 0 15px 30px -5px;
  box-sizing: border-box;
  color: #ffffff;
  display: flex;
  font-family: Phantomsans, sans-serif;
  font-size: 20px;
  justify-content: center;
  line-height: 2em;
  max-width: 100%;
  min-width: 140px;
  padding: 3px;
  text-decoration: none;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  white-space: nowrap;
  cursor: pointer;
}

.button64:active,
.button64:hover {
  outline: 0;
}

.button64 span {
  background-color: rgb(5, 6, 45);
  padding: 16px 24px;
  border-radius: 6px;
  width: 100%;
  height: 100%;
  transition: 300ms;
}

.button64:hover span {
  background: none;
}

@media (min-width: 768px) {
  .button64 {
    font-size: 24px;
    min-width: 196px;
  }
}

/* CSS */
.button49,
.button49:after {
  width: 150px;
  height: 76px;
  line-height: 78px;
  font-size: 20px;
  font-family: 'Bebas Neue', sans-serif;
  background: linear-gradient(45deg, transparent 5%, #ff01ee 5%);
  border: 0;
  color: #fff;
  letter-spacing: 3px;
  box-shadow: 6px 0px 0px #00E6F6;
  outline: transparent;
  position: relative;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
}

.button49:after {
  --slice-0: inset(50% 50% 50% 50%);
  --slice-1: inset(80% -6px 0 0);
  --slice-2: inset(50% -6px 30% 0);
  --slice-3: inset(10% -6px 85% 0);
  --slice-4: inset(40% -6px 43% 0);
  --slice-5: inset(80% -6px 5% 0);

  content: '获取你的 NFT';
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(45deg, transparent 3%, #00E6F6 3%, #00E6F6 5%, #c401ff 5%);
  text-shadow: -3px -3px 0px #F8F005, 3px 3px 0px #00E6F6;
  clip-path: var(--slice-0);
}

.button49:hover:after {
  animation: 1s glitch;
  animation-timing-function: steps(2, end);
}

@keyframes glitch {
  0% {
    clip-path: var(--slice-1);
    transform: translate(-20px, -10px);
  }
  10% {
    clip-path: var(--slice-3);
    transform: translate(10px, 10px);
  }
  20% {
    clip-path: var(--slice-1);
    transform: translate(-10px, 10px);
  }
  30% {
    clip-path: var(--slice-3);
    transform: translate(0px, 5px);
  }
  40% {
    clip-path: var(--slice-2);
    transform: translate(-5px, 0px);
  }
  50% {
    clip-path: var(--slice-3);
    transform: translate(5px, 0px);
  }
  60% {
    clip-path: var(--slice-4);
    transform: translate(5px, 10px);
  }
  70% {
    clip-path: var(--slice-2);
    transform: translate(-10px, 10px);
  }
  80% {
    clip-path: var(--slice-5);
    transform: translate(20px, -10px);
  }
  90% {
    clip-path: var(--slice-1);
    transform: translate(-10px, 0px);
  }
  100% {
    clip-path: var(--slice-1);
    transform: translate(0);
  }
}

@media (min-width: 768px) {
  .button49,
  .button49:after {
    width: 200px;
    height: 86px;
    line-height: 88px;
  }
}

第5步:运行网站并进行测试

当你运行此命令时,这应该会启动与你的钱包的连接。一旦你批准连接,它应该看起来类似于:

npm run dev

website preview

第6步:添加转移加密货币的能力

现在我们已经连接了钱包,我们可以将资金从我们的钱包转移到其他钱包。确保你在钱包中有一些 Mumbai MATIC。你可以在这里获取:https://faucet.polygon.technology

现在我们将创建转账的输入字段和发送按钮。在 pages/ 目录中创建一个新文件并命名为 RareSend.tsx。复制并粘贴以下代码。解释在代码注释中。

import { parseEther } from "ethers/lib/utils.js";
import React, { useState } from "react";
import { useDebounce } from "use-debounce";
import { usePrepareSendTransaction, useSendTransaction } from "wagmi";
import styles from "@/styles/Home.module.css";

// 如果传递的值为 false,这个函数会简单地禁用 SendFunds 函数
interface SendFundsProps {
  disabled?: boolean;
}

export default function SendFunds(props: SendFundsProps) {
  // 声明两个状态变量用于收款人和金额
  const [to, setTo] = useState("");
  const [debouncedTo] = useDebounce(to, 500); // useDebounce() hook 延迟收款人输入500ms

  const [amount, setAmount] = useState("");
  const [debouncedAmount] = useDebounce(amount, 500); // useDebounce() hook 延迟金额输入500ms

  // 使用 usePrepareSendTransaction() hook 准备事务请求
  const { config } = usePrepareSendTransaction({
    request: {
      to: debouncedTo, // 从延迟输入获取收款人
      value: debouncedAmount ? parseEther(debouncedAmount) : undefined, // 从延迟输入获取金额
    },
  });

  // 使用 useSendTransaction() hook 创建事务并发送
  const { sendTransaction } = useSendTransaction(config);

  return (
    <>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          sendTransaction?.(); // 如果 sendTransaction 被定义,则执行
        }}
      >
        <div className={styles.inputcontainer}>
          <input
            aria-label="Recipient"
            onChange={(e) => setTo(e.target.value)} // 输入更改时更新收款人状态
            placeholder="地址目标"
            value={to}
            className={styles.myinput}
          />
          <input
            aria-label="Amount (ether)"
            onChange={(e) => setAmount(e.target.value)} // 输入更改时更新金额状态
            placeholder="输入金额"
            value={amount}
            className={styles.myinput}
          />
        </div>
        <div className={styles.buttoncontainer}>
          <button
            disabled={!sendTransaction || !to || !amount}
            className={styles.button64}
          >
            发送
          </button>{" "}
          {/* 必填字段为空时禁用按钮 */}
        </div>
      </form>
    </>
  );
}

useDebounce() 解释

为了避免过载 RPC 并且被限速,我们将限制使用 usePrepareContractWrite hook,该 hook 会在组件挂载和参数修改时请求 gas 估算。我们在组件中使用 useDebounce hook 来延迟更新 token ID, 如果没有关 键已更改,延迟为500ms。

第7步:插入 sendFunds 组件

接下来,简单地将其添加到 pages/index.tsx 中,如下所示。

import { useAccount, useConnect } from "wagmi";
import SendFunds from "./RareSend";
import { useEffect } from "react";
import styles from "@/styles/Home.module.css";

export default function Home() {
  {
    /* 一些代码... */
  }

  return (
    <>
      <div>
        {/* 一些代码... */}

        {/* 发送资金 */}
        <SendFunds disabled={!isConnected} />

        {/* 铸造 nft */}
      </div>
    </>
  );
}

只要你的地址中有 Matic,你就可以将其发送到另一个账户!

这就是你的网站应该看起来的样子:

react js send cryptocurrency

恭喜你来到这个步骤!你已经成功创建了自己的网站,可以将资金从你的钱包发送到另一个账户。你现在能够轻松地在账户之间转移加密货币,而无需依赖于第三方服务或手动输入交易详细信息。干得好!就这么简单!

Wagmi 示例 第2部分:使用 React + Wagmi 铸造 NFT

我们假设你已经将 NFT 智能合约 部署到区块链上。你可以遵循此视频教程来完成此操作:<https://youtu.be/LIoFbudNVZs>。

NFTs of AI generated cats

AI 生成的猫 NFT

铸造函数可以如下创建。创建一个 mint.tsx 文件,并添加以下代码。

// 初始化 ethers.js 和 wagmi 依赖
import { ethers } from "ethers";
import * as React from "react";
import {
  usePrepareContractWrite,
  useContractWrite,
  useWaitForTransaction,
} from "wagmi";
import styles from "@/styles/Home.module.css";

// 定义铸造 NFT 的 React 函数组件
export function MintNFT() {
  // 准备合约写入配置,提供合约地址、ABI、函数名称和重载
  const { config } = usePrepareContractWrite({
    address: "你的合约地址",
    abi: [
      {
        name: "mint",
        type: "function",
        stateMutability: "payable",
        inputs: [],
        outputs: [],
      },
    ],
    functionName: "mint",
    overrides: {
      from: "你的钱包地址",
      value: ethers.utils.parseEther("0.000000001"), // 整数值应符合 NFT 铸造要求
    },
  });

  // 使用 useContractWrite hook 来写入合约的 mint 函数并获得交易数据和写入函数
  const { data, write } = useContractWrite(config);

  // 使用 useWaitForTransaction hook 等待交易被挖掘并返回加载和成功状态
  const { isLoading, isSuccess } = useWaitForTransaction({
    hash: data?.hash,
  });

  // 渲染组件,带有一个按钮,当点击时触发铸造交易,交易进行时显示加载信息,并在交易成功时显示成功信息
  return (
    &lt;div className={styles.mintcontainer}>
      &lt;button
        disabled={!write || isLoading}
        onClick={() => write?.()}
        className={styles.button49}
      >
        {isLoading ? "铸造中..." : "铸造"}
      &lt;/button>
      {isSuccess && (
        &lt;div>
          成功铸造你的 NFT!
          &lt;div>
            &lt;a href={`https://etherscan.io/tx/${data?.hash}`}>Etherscan&lt;/a>
          &lt;/div>
        &lt;/div>
      )}
    &lt;/div>
  );
}

现在你可以简单地将其插入到 index.tsx 文件中,如下所示:

import { useAccount, useConnect } from "wagmi";
import SendFunds from "./RareSend";
import { useEffect } from "react";
import styles from "@/styles/Home.module.css";
import { MintNFT } from "./mint";

export default function Home() {
  const { connect, connectors } = useConnect();
  const { isConnected } = useAccount();

  useEffect(() => {
    console.log(
      `当前连接状态: ${isConnected ? "已连接" : "未连接"}`
    );
  }, [isConnected]);

  return (
    &lt;>
      &lt;p
        className={styles.status}
        style={{
          color: isConnected ? "green" : "red",
        }}
      >
        {" "}
        {isConnected !== undefined
          ? isConnected
            ? "已连接"
            : "未连接"
          : "加载中..."}
      &lt;/p>
      &lt;div className={styles.maincontainer}>
        &lt;div className={styles.container}>
          &lt;div className={styles.buttonswrapper}>
            &lt;div className={styles.buttonswrapperGrid}>
              {connectors.map((connector) => (
                &lt;button
                  suppressHydrationWarning
                  key={connector.id}
                  onClick={() => connect({ connector })}
                  className={styles.button28}
                >
                  {connector.name}
                &lt;/button>
              ))}
            &lt;/div>
          &lt;/div>
        &lt;/div>

        {/* 发送资金 */}
        &lt;SendFunds disabled={!isConnected} />

        {/* 铸造 nft */}
        &lt;MintNFT />
      &lt;/div>
    &lt;/>
  );
}

你的最终网站应看起来类似于:

wagmi react js mint nft transfer crypto

恭喜你!你刚刚创建了自己的去中心化应用程序,具备发送交易和铸造 NFT 的能力。

最初发布于 2023 年 4 月 24 日

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

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/