🎉 #Gate Alpha 第三届积分狂欢节 & ES Launchpool# 聯合推廣任務上線!
本次活動總獎池:1,250 枚 ES
任務目標:推廣 Eclipse($ES)Launchpool 和 Alpha 第11期 $ES 專場
📄 詳情參考:
Launchpool 公告:https://www.gate.com/zh/announcements/article/46134
Alpha 第11期公告:https://www.gate.com/zh/announcements/article/46137
🧩【任務內容】
請圍繞 Launchpool 和 Alpha 第11期 活動進行內容創作,並曬出參與截圖。
📸【參與方式】
1️⃣ 帶上Tag #Gate Alpha 第三届积分狂欢节 & ES Launchpool# 發帖
2️⃣ 曬出以下任一截圖:
Launchpool 質押截圖(BTC / ETH / ES)
Alpha 交易頁面截圖(交易 ES)
3️⃣ 發布圖文內容,可參考以下方向(≥60字):
簡介 ES/Eclipse 項目亮點、代幣機制等基本信息
分享你對 ES 項目的觀點、前景判斷、挖礦體驗等
分析 Launchpool 挖礦 或 Alpha 積分玩法的策略和收益對比
🎁【獎勵說明】
評選內容質量最優的 10 位 Launchpool/Gate
SIWE集成指南:提升Dapp用戶身分驗證
SIWE 使用指南:增強你的 Dapp 功能
SIWE(Sign-In with Ethereum)是一種在以太坊上驗證用戶身分的方式,類似於發起交易,證明用戶對錢包的控制權。目前大多數錢包插件都支持這種簡單的身分驗證方式,只需在插件中籤名即可。
本文主要討論以太坊上的籤名場景,不涉及其他公鏈如 Solana、SUI 等。
你的項目是否需要 SIWE?
如果你的 Dapp 有以下需求,可以考慮使用 SIWE:
但如果你的 Dapp 主要是查詢功能,類似 etherscan,則可以不使用 SIWE。
你可能會問,在 Dapp 上連接錢包後,不就表明了錢包所有權嗎?這個說法不完全準確。對前端來說,連接錢包確實表明了身分,但對需要後端支持的接口調用,僅傳遞地址是不足以證明身分的,因爲地址是公開信息,任何人都可以使用。
SIWE 的原理和流程
SIWE 的流程可以概括爲三個步驟:連接錢包、籤名、獲取身分標識。
連接錢包
這是常見的 Web3 操作,通過錢包插件在 Dapp 中連接錢包。
籤名
SIWE 的籤名步驟包括獲取 Nonce 值、錢包籤名和後端籤名校驗。
獲取 Nonce 值需調用後端接口。後端生成隨機 Nonce 值並與當前地址關聯,爲後續籤名做準備。
前端獲取 Nonce 值後,構建籤名內容,包括 Nonce 值、域名、鏈 ID 和籤名內容等,通常使用錢包提供的方法進行籤名。
構建完籤名後,將其發送給後端。
獲取身分標識
後端校驗籤名通過後,返回用戶身分標識,如 JWT。前端後續請求時攜帶地址和身分標識,即可證明錢包所有權。
實踐操作
我們將使用 Next.js 開發一個全棧應用,集成 SIWE 功能。注意,此 demo 僅用於展示基本流程,不適合直接用於生產環境。
準備工作
確保已安裝 Node.js 環境。
安裝依賴
首先安裝 Next.js:
npx create-next-app@14
按提示完成安裝。
進入項目目錄,運行項目:
npm run dev
訪問 localhost:3000 可以看到基本的 Next.js 項目。
安裝 SIWE 相關依賴
我們選擇使用 Ant Design Web3,因爲它免費、持續維護、使用體驗良好,且支持 SIWE。
安裝依賴:
npm install antd @ant-design/web3 @ant-design/web3-wagmi wagmi viem @tanstack/react-query --save
引入 Wagmi
在 layout.tsx 中引入相關 Provider:
typescript "use client"; import { getNonce, verifyMessage } from "@/app/api"; import { Mainnet, MetaMask, OkxWallet, TokenPocket, WagmiWeb3ConfigProvider, WalletConnect, } from "@ant-design/web3-wagmi"; import { QueryClient } from "@tanstack/react-query"; import React from "react"; import { createSiweMessage } from "viem/siwe"; import { http } from "wagmi"; import { JwtProvider } from "./JwtProvider";
const YOUR_WALLET_CONNECT_PROJECT_ID = "c07c0051c2055890eade3556618e38a6"; const queryClient = new QueryClient();
const WagmiProvider: React.FC = ({ children }) => { const [jwt, setJwt] = React.useState(null);
return ( <wagmiweb3configprovider siweconfig="{{" getnonce:="" async="" (address)=""> (await getNonce(address)).data, createMessage: (props) => { return createSiweMessage({ ...props, statement: "Ant Design Web3" }); }, verifyMessage: async (message, signature) => { const jwt = (await verifyMessage(message, signature)).data; setJwt(jwt); return !!jwt; }, }} chains={[Mainnet]} transports={{ [Mainnet.id]: http(), }} walletConnect={{ projectId: YOUR_WALLET_CONNECT_PROJECT_ID, }} wallets={[ MetaMask(), WalletConnect(), TokenPocket({ group: "Popular", }), OkxWallet(), ]} queryClient={queryClient} > {children} ); };
export default WagmiProvider;
添加連接錢包按鈕:
typescript "use client"; import type { Account } from "@ant-design/web3"; import { ConnectButton, Connector } from "@ant-design/web3"; import { Flex, Space } from "antd"; import React from "react"; import { JwtProvider } from "./JwtProvider";
export default function App() { const jwt = React.useContext(JwtProvider);
const renderSignBtnText = ( defaultDom: React.ReactNode, account?: Account ) => { const { address } = account ?? {}; const ellipsisAddress = address ? ${address.slice(0, 6)}...${address.slice(-6)} : ""; return Sign in as ${ellipsisAddress}; };
return ( <>
接口實現
Nonce
生成隨機 Nonce 並與地址關聯:
typescript import { randomBytes } from "crypto"; import { addressMap } from "../cache";
export async function GET(request: Request) { const { searchParams } = new URL(request.url); const address = searchParams.get("address");
if (!address) { throw new Error("Invalid address"); } const nonce = randomBytes(16).toString("hex"); addressMap.set(address, nonce); return Response.json({ data: nonce, }); }
verifyMessage
驗證籤名並返回 JWT:
typescript import { createPublicClient, http } from "viem"; import { mainnet } from "viem/chains"; import jwt from "jsonwebtoken"; import { parseSiweMessage } from "viem/siwe"; import { addressMap } from "../cache";
const JWT_SECRET = "your-secret-key";
const publicClient = createPublicClient({ chain: mainnet, transport: http(), });
export async function POST(request: Request) { const { signature, message } = await request.json();
const { nonce, address = "0x" } = parseSiweMessage(message);
if (!nonce || nonce !== addressMap.get(address)) { throw new Error("Invalid nonce"); }
const valid = await publicClient.verifySiweMessage({ message, address, signature, });
if (!valid) { throw new Error("Invalid signature"); }
const token = jwt.sign({ address }, JWT_SECRET, { expiresIn: "1h" }); return Response.json({ data: token, }); }
優化建議
爲提高驗證速度,建議使用專門的節點服務。可以使用 ZAN 的節點服務,獲取 RPC 連接後替換代碼中的默認 RPC:
typescript const publicClient = createPublicClient({ chain: mainnet, transport: http('), //ZAN節點服務RPC });
這樣可以顯著提高接口響應速度。