<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Sammie's blog]]></title><description><![CDATA[I do a little bit of Frontend, a little bit of Backend and a little bit of web3 things!]]></description><link>https://blog.samuelchen.me</link><generator>RSS for Node</generator><lastBuildDate>Wed, 06 May 2026 12:49:31 GMT</lastBuildDate><atom:link href="https://blog.samuelchen.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[2023年创建Web3前端项目全教程（Next.js13）]]></title><description><![CDATA[一眨眼2023年年中了，Web3的前端世界迎来了一系列的更新迭代，曾经要想在前端与区块链（EVM兼容）交互只有两个选择 Web3.js 和 Ethers.js，如今多了一个强力的竞争者 - Viem，并且有后来居上的劲头。而我们熟悉的 WalletConnect 也正是弃用了v1，并开始大力推广v2。虽然处在熊市热度不多，但产品的更新迭代反而是更多了。能用的工具太多了，整体的生态也在不断完善，现在开始一个Web3项目从来没有如此简单过，这篇文章会交大家怎么用Next.js13新建一个Web3项目...]]></description><link>https://blog.samuelchen.me/2023web3nextjs13</link><guid isPermaLink="true">https://blog.samuelchen.me/2023web3nextjs13</guid><category><![CDATA[Web3]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[#wagmi]]></category><dc:creator><![CDATA[Sammie 逗比国国王]]></dc:creator><pubDate>Fri, 14 Jul 2023 12:53:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1689338930414/815c0245-31e1-4ed6-b46f-3291e63c50f4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>一眨眼2023年年中了，Web3的前端世界迎来了一系列的更新迭代，曾经要想在前端与区块链（EVM兼容）交互只有两个选择 <a target="_blank" href="https://web3js.readthedocs.io/">Web3.js</a> 和 <a target="_blank" href="https://docs.ethers.org/">Ethers.js</a>，如今多了一个强力的竞争者 - <a target="_blank" href="https://viem.sh/">Viem</a>，并且有后来居上的劲头。而我们熟悉的 <a target="_blank" href="https://walletconnect.com/">WalletConnect</a> 也正是弃用了v1，并开始大力推广v2。虽然处在熊市热度不多，但产品的更新迭代反而是更多了。能用的工具太多了，整体的生态也在不断完善，现在开始一个Web3项目从来没有如此简单过，这篇文章会交大家怎么用Next.js13新建一个Web3项目，并且会分享途中遇到的一些问题的解决办法，帮助大家快速起步！</p>
<h2 id="heading-6ycj5oup6le5o6l6zkx5yyf5os5lu2">选择连接钱包插件</h2>
<p>市面上的钱包产品多不胜数，要想与每一个钱包对接当然是可行的，但对于大部分的项目来说与其从零开始造轮子不如用现有的解决办法。根据我的观察市面上一共有三款成熟的解决方案可以选择，下面我们分别介绍一下。</p>
<h3 id="heading-wallet-connect-web3modal">Wallet Connect - Web3Modal</h3>
<p><a target="_blank" href="https://walletconnect.com/">https://walletconnect.com/</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689243360566/da06603a-25f7-405b-af1c-fdd374379278.png" alt class="image--center mx-auto" /></p>
<p>作为一个老牌的Protocol，它们支持了超过三百个钱包，并且为开发者提供了非常方便的SDK和文档。最近也正式弃用v1，正式推广v2了。不仅仅如此，曾经红极一时的一款 叫做<a target="_blank" href="https://web3modal.com/">Web3Modal</a> 的开源项目也被WalletConnect收购了，随着而来的是推出了更加强大和个性化的Web3Modal v2。</p>
<p>Web3Modal有分为普通的html版本和React版本，所以理论上支持任何框架。支持如果用React会更方便而已。React版本简历在 <a target="_blank" href="https://wagmi.sh/">Wagmi</a> 和前面提到的 <a target="_blank" href="https://viem.sh/">Viem</a> 之上，除此之外只需要简单几步就能为你的dApp带来连接钱包的功能并且通过Wagmi开放了非常多的React hook。</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689243769712/c3e0a1d3-3f4a-4d01-8a8e-542b6a0244da.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-connectkit-by-family">ConnectKit by Family</h3>
<p><a target="_blank" href="https://github.com/family/connectkit">https://github.com/family/connectkit</a></p>
<p>同样背靠大公司，ConnectKit作为一个开源项目，同样也是建立在 <a target="_blank" href="https://wagmi.sh/">Wagmi</a> 和 <a target="_blank" href="https://viem.sh/">Viem</a> 上，并且默认支持 WalletConnect，所以理论上WalletConnect支持的他们也支持。遗憾的是ConnectKit暂时只支持React，但是对比Web3Modal最大的优点就是简洁！实在是太简洁了，而且使用体验丝滑，且比较容易定制化来迎合你的产品的branding。</p>
<p>其他真的找不出啥大区别，完全取决于你的个人喜好了</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689244561025/422a3f6a-291c-4304-96f4-e38cd36c96d1.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-rainbowkit">RainbowKit</h3>
<p><a target="_blank" href="https://www.rainbowkit.com/">https://www.rainbowkit.com/</a></p>
<p>最后就是Rainbow🌈钱包推出的RainbowKit，最新版本也是建立在建立在 <a target="_blank" href="https://wagmi.sh/">Wagmi</a> 和 <a target="_blank" href="https://viem.sh/">Viem</a> 上，同样支持WalletConnect，同样的仅支持React。</p>
<p><img src="https://www.rainbowkit.com/_next/image?url=%2Fhero-modal.png&amp;w=3840&amp;q=75" alt /></p>
<p>选择什么连接钱包的SDK，取决于你用的是什么前端框架，如果不是React，那么还是默认Web3Modal吧。如果是React的话，真的完完全全看个人选择了，因为底层使用的都是Wagmi所以使用体验上都大抵相同。</p>
<h3 id="heading-6ycj5oup6zo5lik5lqk5lqs5os5lu2">选择链上交互插件</h3>
<p>大概在一年前基本只有两个选择：<a target="_blank" href="https://web3js.readthedocs.io/">Web3.js</a> 和 <a target="_blank" href="https://docs.ethers.org/">Ethers.js</a>，前者非常老了且非常难用，后者稍微新一点但是也已经有点过时了，两个包尺寸都偏大且非常不现代化。幸好现在有了 <a target="_blank" href="https://viem.sh/">Viem</a> ，一个只有 35kB、拥有100% test covergae、且有TS support的开源包，实在是无可挑剔。如果你使用的是React，那么可以直接使用 <a target="_blank" href="https://wagmi.sh/">Wagmi</a>，因为它直接就提供了多个现成的React hook使用，且底层也是使用的Viem。</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">👀</div>
<div data-node-type="callout-text">其实 Viem是由Wagmi开发，在Wagmi旧的版本其实使用的是 Ethers.js，但苦于 ethers.js属于维护与更新，所以他们团队决定自己开发一个与EVM链交互的包，事实证明他们也成功了。</div>
</div>

<p>选择什么包，目前个人默认推荐的是 Viem，如果是使用React，直接 Wagmi + Viem走起！</p>
<h2 id="heading-5paw5bu66ag555uu">新建项目</h2>
<h3 id="heading-nextjs">新建Next.js项目</h3>
<p>准备工作做好，我们也正式开始新建项目，我们在这里会使用最新的 Next13 来新建项目，并且使用 Next 最新的 App directory，我们在这里会按照 Next.js 的 <a target="_blank" href="https://nextjs.org/docs">官方文档</a> 的步骤走。</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">无论如何你平时喜欢用什么框架，React 和 Next.js目前来说基本就是Web3项目标配，因为能用的插件太多了，让我们不需要从头造轮子。</div>
</div>

<p>我们新建好项目文件夹，直接在终端输入一下以下指令</p>
<pre><code class="lang-bash">npx create-next-app@latest
</code></pre>
<p>接着根据自己项目的需要填写项目的配置，个人推荐使用TypeScript、和App Router。</p>
<pre><code class="lang-bash">What is your project named? my-app
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 <span class="hljs-built_in">alias</span>? No / Yes
</code></pre>
<p>安装完成后我们就正式有一个Next13的项目了。</p>
<h3 id="heading-5a6j6kof6le5o6l6zkx5yyf5os5lu2">安装连接钱包插件</h3>
<p>接着我们安装前面选定的连接钱包插件，这里以 WalletConnect 的 Web3Modal React 版本为例（官方文档 <a target="_blank" href="https://docs.walletconnect.com/2.0/web/web3modal/react/wagmi/installation">这里</a>）。</p>
<p>我们继续在终端运行指令，确保已进入项目的根目录，运行以下指令安装包</p>
<pre><code class="lang-bash">npm install @web3modal/ethereum @web3modal/react wagmi viem
</code></pre>
<p>可以看到我们不仅仅安装了Web3Modal，也安装了Wagmi和Viem，所以后续如果有需要与合约交互等久不需要安装额外的包了，直接使用Wagmi或者Viem即可。</p>
<p>新版本的WalletConnect要求所有项目都得申请一个project id，不过很简单，我们只需要在WalletConnect的后台新建一个项目即可获取。</p>
<p>后台地址：<a target="_blank" href="https://cloud.walletconnect.com/app">https://cloud.walletconnect.com/app</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689290955633/0cd5687d-529a-4527-988c-ab1a2cac3970.png" alt class="image--center mx-auto" /></p>
<p>获取到project id后，我们回到项目根目录新建一个 <code>.env.local</code> 文件夹，用于存储我们本地开发的环境变量。</p>
<p>路径：<code>./.env.local</code></p>
<pre><code class="lang-bash">NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=<span class="hljs-string">"YOUR_PROJECT_ID"</span>
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">注意需要在前面加上 <code>NEXT<em>PUBLIC</em></code> 这样子这个环境变量才能被前端读取</div>
</div>

<h3 id="heading-web3modalwagmi">配置Web3Modal和Wagmi</h3>
<p>东西都安装好了，接下来我们可以在项目内配置Web3Modal，不过按照官方文档直接在<code>layout.tsx</code> 添加配置是会报错的，因为Next.js默认是服务器渲染，而我们使用的任何连接钱包的插件都是仅支持客户端渲染（因为需要读取前端浏览器内的钱包插件），解决办法也比较容易，我们需要把所有相关的Config统一放在一个独立的component，然后通过最新的 "use client"来标记其为仅客户端渲染。</p>
<p>接下来，新建一个文件，这里我根据自己的喜好放在下面的路径 👇🏻</p>
<p>路径：<code>./app/_providers/Web3ModalProvider.tsx</code></p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>
<span class="hljs-keyword">import</span> { EthereumClient, w3mConnectors, w3mProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'@web3modal/ethereum'</span>
<span class="hljs-keyword">import</span> { Web3Modal } <span class="hljs-keyword">from</span> <span class="hljs-string">'@web3modal/react'</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { configureChains, createConfig, WagmiConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'wagmi'</span>
<span class="hljs-keyword">import</span> { arbitrum, mainnet, polygon } <span class="hljs-keyword">from</span> <span class="hljs-string">'wagmi/chains'</span>

<span class="hljs-keyword">const</span> chains = [arbitrum, mainnet, polygon]
<span class="hljs-keyword">const</span> projectId = process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID

<span class="hljs-keyword">type</span> Web3ModalProviderType = {
    children: React.ReactNode
};

<span class="hljs-keyword">const</span> { publicClient } = configureChains(chains, [w3mProvider({ projectId })])
<span class="hljs-keyword">const</span> wagmiConfig = createConfig({
    autoConnect: <span class="hljs-literal">true</span>,
    connectors: w3mConnectors({ projectId, chains }),
    publicClient
})
<span class="hljs-keyword">const</span> ethereumClient = <span class="hljs-keyword">new</span> EthereumClient(wagmiConfig, chains)

<span class="hljs-keyword">const</span> Web3ModalProvider = <span class="hljs-function">(<span class="hljs-params">{ children }: Web3ModalProviderType</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;WagmiConfig config={wagmiConfig}&gt;
                {children}
            &lt;/WagmiConfig&gt;
            &lt;Web3Modal projectId={projectId} ethereumClient={ethereumClient}/&gt;
        &lt;/&gt;
    )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Web3ModalProvider
</code></pre>
<p>接下来需要修改根layout文件，引入新建的Web3ModalProvider，修改后我们的layout应该长下面这样</p>
<p>路径：<code>./app/layout.tsx</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-string">'./globals.css'</span>
<span class="hljs-keyword">import</span> Web3ModalProvider <span class="hljs-keyword">from</span> <span class="hljs-string">'./_providers/Web3ModalProvider'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata = {
  title: <span class="hljs-string">'Your project'</span>,
  description: <span class="hljs-string">'Your project description'</span>,
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RootLayout</span>(<span class="hljs-params">{
  children,
}: {
  children: React.ReactNode
}</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;html lang=<span class="hljs-string">"en"</span>&gt;
      &lt;body&gt;
        &lt;Web3ModalProvider&gt;
          {children}
        &lt;/Web3ModalProvider&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  )
}
</code></pre>
<p>最后的最后，我们把默认的 <code>page.tsx</code> 清理一下，看看行不行。</p>
<p>路径：<code>./app/page.tsx</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
         Hello World
    &lt;/div&gt;
  )
}
</code></pre>
<p>我们运行指令开启本地开发环境</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<p>可以看到页面正常加载了，且没有报错，太棒了！</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689336473618/c4a3aed2-38be-45b5-a94d-a6f2d60482bd.png" alt class="image--center mx-auto" /></p>
<p>在前端看起来没问题，但是在终端内却出现了非常多类似下面这样的报错，经过一番研究应该是新版本的Next13无法区分客户端和服务器端的依赖，不知道未来是否会修复，但是不影响开发使用。</p>
<pre><code class="lang-bash">warn  - ./node_modules/@walletconnect/keyvaluestorage/dist/cjs/node-js/db.js
Module not found: Can<span class="hljs-string">'t resolve '</span>lokijs<span class="hljs-string">' in '</span>/home/runner/walletconnect-issue/node_modules/@walletconnect/keyvaluestorage/dist/cjs/node-js<span class="hljs-string">'

Import trace for requested module:
./node_modules/@walletconnect/keyvaluestorage/dist/cjs/node-js/db.js
./node_modules/@walletconnect/keyvaluestorage/dist/cjs/node-js/index.js
./node_modules/@walletconnect/keyvaluestorage/dist/cjs/index.js
./node_modules/@walletconnect/sign-client/node_modules/@walletconnect/core/dist/index.cjs.js
./node_modules/@walletconnect/sign-client/dist/index.cjs.js
./node_modules/@walletconnect/universal-provider/dist/index.cjs.js
./node_modules/@wagmi/connectors/dist/walletConnect.js
./node_modules/@wagmi/core/dist/connectors/walletConnect.js
./node_modules/wagmi/dist/connectors/walletConnect.js
./app/(components)/WagmiProvider.tsx

./node_modules/cross-fetch/node_modules/node-fetch/lib/index.js
Module not found: Can'</span>t resolve <span class="hljs-string">'encoding'</span> <span class="hljs-keyword">in</span> <span class="hljs-string">'/home/runner/walletconnect-issue/node_modules/cross-fetch/node_modules/node-fetch/lib'</span>

Import trace <span class="hljs-keyword">for</span> requested module:
./node_modules/cross-fetch/node_modules/node-fetch/lib/index.js
./node_modules/cross-fetch/dist/node-ponyfill.js
./node_modules/@walletconnect/jsonrpc-http-connection/dist/cjs/http.js
./node_modules/@walletconnect/jsonrpc-http-connection/dist/cjs/index.js
./node_modules/@walletconnect/ethereum-provider/dist/cjs/index.js
./node_modules/@wagmi/connectors/dist/walletConnect.js
./node_modules/@wagmi/core/dist/connectors/walletConnect.js
./node_modules/wagmi/dist/connectors/walletConnect.js
./app/(components)/WagmiProvider.tsx

./node_modules/pino/lib/tools.js
Module not found: Can<span class="hljs-string">'t resolve '</span>pino-pretty<span class="hljs-string">' in '</span>/home/runner/walletconnect-issue/node_modules/pino/lib<span class="hljs-string">'

Import trace for requested module:
./node_modules/pino/lib/tools.js
./node_modules/pino/pino.js
./node_modules/@walletconnect/universal-provider/dist/index.cjs.js
./node_modules/@wagmi/connectors/dist/walletConnect.js
./node_modules/@wagmi/core/dist/connectors/walletConnect.js
./node_modules/wagmi/dist/connectors/walletConnect.js
./app/(components)/WagmiProvider.tsx</span>
</code></pre>
<p>要想消除错误，只需要修改一下next config。</p>
<p>路径：<code>./next.config.js</code></p>
<pre><code class="lang-bash">/** @<span class="hljs-built_in">type</span> {import(<span class="hljs-string">'next'</span>).NextConfig} */
const nextConfig = {
  experimental: {
    appDir: <span class="hljs-literal">true</span>,
  },
  webpack: (config) =&gt; {
    config.externals.push(<span class="hljs-string">"pino-pretty"</span>, <span class="hljs-string">"lokijs"</span>, <span class="hljs-string">"encoding"</span>);
    <span class="hljs-built_in">return</span> config;
  },
};

module.exports = nextConfig;
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">在项目导入.env环境变量的时候如果看到类似下面这样的错误，我们只需要根目录新建一个additional-env.d.ts告诉TS我们它我们额外的环境变量即可消除这个报错。</div>
</div>

<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689336550093/4bec8441-5cda-440b-b9d5-69aa3309400a.png" alt class="image--center mx-auto" /></p>
<p>路径：<code>./additional-env.d.ts</code></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">declare</span> <span class="hljs-built_in">global</span> {
  <span class="hljs-keyword">namespace</span> NodeJS {
    <span class="hljs-keyword">interface</span> ProcessEnv {
      NODE_ENV: <span class="hljs-string">"development"</span> | <span class="hljs-string">"production"</span>;
      NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID: <span class="hljs-built_in">string</span>;
    }
  }
}

<span class="hljs-keyword">export</span> {};
</code></pre>
<h3 id="heading-5re75yqg6le5o6l6zkx5yyf5oyj6zku">添加连接钱包按钮</h3>
<p>接下来就是添加连接钱包的按钮了，Web3Modal直接开盒提供了现成的按钮，我们直接导入就好。同理由于连接钱包按钮仅支持客户端渲染，我们不能直接在 <code>page.tsx</code> 导入，需要新建一个client component。</p>
<p>路径：<code>./app/(components)/ConnectWalletBtn.tsx</code></p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>
<span class="hljs-keyword">import</span> { Web3Button } <span class="hljs-keyword">from</span> <span class="hljs-string">'@web3modal/react'</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ConnectWalletBtn</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> &lt;Web3Button /&gt;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ConnectWalletBtn
</code></pre>
<p>历经了这么多问题，我们终于能看到 Connect Wallet按钮了！！！点击后也能正常连接钱包，太棒了！</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689337880508/27cfaaeb-d4ed-4033-aaec-05b5437a8217.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1689337935610/f9ba31f0-395e-4074-8b1e-d027dd94526e.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-5pya5zco55qe5pya5zco">最后的最后</h2>
<p>到此为止，我们成功地新建了一个Next.js13的项目，并且成功配置了Web3Modal (WalletConnect)、Wagmi和Viem，且能正常连接钱包了！！！虽然途中遇到了一些问题，但我们也顺利解决了。这篇文章可以看起来不够清晰，我已经把本篇文章的Sample项目建了一个仓库，需要自取 👇🏻</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/DbgKinggg/next13-web3-modal-starter-kit">https://github.com/DbgKinggg/next13-web3-modal-starter-kit</a></div>
<p> </p>
<p>最后，非常感谢大家的支持！祝大家buidl愉快~</p>
]]></content:encoded></item></channel></rss>