📢 Gate Square Exclusive: #PUBLIC Creative Contest# Is Now Live!
Join Gate Launchpool Round 297 — PublicAI (PUBLIC) and share your post on Gate Square for a chance to win from a 4,000 $PUBLIC prize pool
🎨 Event Period
Aug 18, 2025, 10:00 – Aug 22, 2025, 16:00 (UTC)
📌 How to Participate
Post original content on Gate Square related to PublicAI (PUBLIC) or the ongoing Launchpool event
Content must be at least 100 words (analysis, tutorials, creative graphics, reviews, etc.)
Add hashtag: #PUBLIC Creative Contest#
Include screenshots of your Launchpool participation (e.g., staking record, reward
SIWE aids DApp identification to create a secure and reliable user system.
SIWE: A more powerful method of identification for your DApp
SIWE(Sign-In with Ethereum) is a method for verifying user identification on Ethereum, similar to initiating a transaction to prove the user's control over the wallet. Currently, most wallet plugins support this simple authentication method, requiring only a signature of the information within the plugin. This article primarily discusses the signing scenarios on Ethereum and does not cover other public chains.
Does the project need SIWE?
If your DApp has the following requirements, you may consider using SIWE:
But if your DApp mainly serves as a query function, like applications such as etherscan, then SIWE may not be necessary.
Although connecting a wallet on a DApp represents ownership of the wallet, this is only valid for the front end. For interface calls that require back-end support, simply passing the address cannot prove identification, as the address is public information.
The Principle and Process of SIWE
The SIWE process can be summarized in three steps: connect wallet - sign - obtain identification.
Connect Wallet
This is a common Web3 operation to connect the wallet in the DApp through the wallet plugin.
signature
The signing steps include obtaining the Nonce value, wallet signing, and backend signature verification.
First, call the backend interface to obtain the Nonce value. The backend will generate a random Nonce and associate it with the address. After obtaining the Nonce, the front end constructs the signature content, including Nonce, domain name, chain ID, etc., uses the method provided by the wallet to sign, and then sends the signature to the backend.
obtain identification
After the backend verification of the signature passes, it will return the user's identification, such as JWT. The frontend should include the address and identification in subsequent requests to prove ownership of the wallet.
Practice SIWE
We will develop a DApp that supports SIWE using Next.js, with the goal of returning a JWT for identification verification.
preparation work
npx create-next-app@14
npm install antd @ant-design/web3 @ant-design/web3-wagmi wagmi viem @tanstack/react-query --save
Introduce Wagmi
Import WagmiProvider in layout.tsx:
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 siwe="{{" 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} > <jwtprovider.provider value="{jwt}">{children}</jwtprovider.provider> ); };
export default WagmiProvider;
add connection button
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 ( <>
implement interface
Nonce interface
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, }); }
verification message interface
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, }); }
Optimization Suggestions
To improve the response speed of SIWE login, it is recommended to use a dedicated node service instead of the default RPC node. Taking the ZAN node service as an example, after obtaining the Ethereum mainnet HTTPS RPC connection, replace the default RPC of publicClient:
typescript const publicClient = createPublicClient({ chain: mainnet, transport: http('), //ZAN node service RPC });
This can significantly reduce verification time and improve interface speed.