稳健,是 Gate 持续增长的核心动力。
真正的成长,不是顺风顺水,而是在市场低迷时依然坚定前行。我们或许能预判牛熊市的大致节奏,但绝无法精准预测它们何时到来。特别是在熊市周期,才真正考验一家交易所的实力。
Gate 今天发布了2025年第二季度的报告。作为内部人,看到这些数据我也挺惊喜的——用户规模突破3000万,现货交易量逆势环比增长14%,成为前十交易所中唯一实现双位数增长的平台,并且登顶全球第二大交易所;合约交易量屡创新高,全球化战略稳步推进。
更重要的是,稳健并不等于守成,而是在面临严峻市场的同时,还能持续创造新的增长空间。
欢迎阅读完整报告:https://www.gate.com/zh/announcements/article/46117
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 });
这样可以显著提高接口响应速度。