如何在 Web3 中构建持久的社交形象
2025 年 1 月 31 日
-
通过 BlockEden.xyz 的高吞吐量 RPC 端点,确保你的链上社交体验始终流畅。
-
采用代币门控、Lens、Farcaster、Farcaster Frames、Mirror、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP、POAP
1. 介绍
在 Web3 生态系统中,构建可扩展且安全的社交网络是一个关键挑战。传统的中心化平台在数据隐私、内容审查和平台垄断方面存在显著局限。相反,去中心化的社交网络通过区块链技术提供了更高的透明度、用户主权和抗审查性。
1.1 为什么选择去中心化社交网络?
- 数据主权:用户拥有并控制自己的数据,避免平台滥用或泄露。
- 抗审查:去中心化的存储和治理模型使内容更难被单点审查。
- 激励机制:通过代币经济模型,用户可以因内容创作、互动和社区治理获得奖励。
1.2 关键技术栈
- 区块链:以太坊、Polygon、Arbitrum 等 EVM 兼容链,用于身份验证、代币经济和治理。
- 去中心化存储:IPFS、Filecoin、Arweave,用于永久存储内容和媒体文件。
- 身份协议:ENS、Lens、Worldcoin、World ID,用于去中心化身份(DID)管理。
- 消息协议:XMTP、Push Protocol、Waku,用于点对点消息传递和通知。
- 前端框架:Next.js、React、Tailwind CSS,用于构建响应式 UI。
- 后端:Node.js、Express、GraphQL、The Graph,用于 API 聚合和索引链上数据。
2. 架构概览
2.1 高层组件
2.2 数据流
- 用户注册:使用 ENS / Lens 进行去中心化身份验证,生成 DID。
- 内容发布:内容上传至 IPFS,返回 CID;将 CID 写入链上(ERC-721 NFT)或链下(IPFS Pinning Service)。
- 互动:点赞、评论等操作写入链上(ERC-20 代币奖励)或链下(Off-chain DB + Merkle Proof)。
- 消息:使用 XMTP 发送点对点消息,Push Protocol 推送通知。
- 治理:通过 Snapshot 提交提案,使用 DAO 合约执行投票结果。
3. 核心功能实现
3.1 去中心化身份(DID)集成
3.1.1 ENS + Lens
import { useAccount, useConnect, useDisconnect } from 'wagmi';
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { useEffect, useState } from 'react';
import { ethers } from 'ethers';
import ENS from '@ensdomains/ensjs';
export const useENS = () => {
const { address, isConnected } = useAccount();
const [ensName, setEnsName] = useState<string | null>(null);
const [profile, setProfile] = useState<any>(null);
useEffect(() => {
if (!isConnected) return;
const provider = new ethers.providers.Web3Provider(window.ethereum);
const ens = new ENS({ provider, ensAddress: ENS.getEnsAddress('1') });
const fetchENS = async () => {
const name = await ens.getName(address!);
setEnsName(name?.name || null);
if (name?.name) {
const resolver = await ens.getResolver(name.name);
const avatar = await resolver?.getText('avatar');
setProfile({ avatar });
}
};
fetchENS();
}, [address, isConnected]);
return { ensName, profile };
};
3.1.2 World ID 集成(防刷)
import { useWorldID } from '@worldcoin/id';
export const WorldIDVerification = () => {
const { isVerified, verify } = useWorldID({
appId: 'YOUR_WORLD_ID_APP_ID',
action: 'login',
signal: window.location.href,
});
if (!isVerified) {
return <button onClick={verify}>Verify with World ID</button>;
}
return <p>✅ Verified</p>;
};
3.2 内容发布
3.2.1 IPFS 上传
import { create } from 'ipfs-http-client';
import { ethers } from 'ethers';
import PostABI from '@/contracts/Post.json';
const ipfs = create({ url: 'https://ipfs.infura.io:5001/api/v0' });
export const uploadPost = async (content: string, title: string) => {
const file = new Blob([JSON.stringify({ title, content })], {
type: 'application/json',
});
const added = await ipfs.add(file);
return added.path; // CID
};
export const mintPostNFT = async (cid: string, signer: ethers.Signer) => {
const contract = new ethers.Contract(
process.env.NEXT_PUBLIC_POST_CONTRACT!,
PostABI.abi,
signer,
);
const tx = await contract.mintPost(cid);
await tx.wait();
return tx.hash;
};
3.2.2 ERC-721 合约(Post NFT)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
contract PostNFT is ERC721URIStorage {
uint256 public tokenIdCounter;
address public admin;
constructor() ERC721("PostNFT", "POST") {
admin = msg.sender;
}
function mintPost(string memory _cid) external {
uint256 tokenId = tokenIdCounter++;
_safeMint(msg.sender, tokenId);
_setTokenURI(tokenId, string(abi.encodePacked("ipfs://", _cid)));
}
}
3.3 互动(点赞、评论)
3.3.1 ERC-20 奖励代币
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract SocialToken is ERC20 {
address public admin;
mapping(uint256 => uint256) public postLikes;
constructor() ERC20("SocialToken", "SOC") {
admin = msg.sender;
_mint(msg.sender, 1_000_000 * 10 ** decimals());
}
function likePost(uint256 postId) external {
postLikes[postId] += 1;
_transfer(admin, msg.sender, 10 * 10 ** decimals()); // reward
}
}
3.3.2 前端交互
import { useContractWrite, usePrepareContractWrite } from 'wagmi';
import SocialTokenABI from '@/contracts/SocialToken.json';
export const LikeButton = ({ postId }: { postId: number }) => {
const { config } = usePrepareContractWrite({
address: process.env.NEXT_PUBLIC_SOCIAL_TOKEN!,
abi: SocialTokenABI.abi,
functionName: 'likePost',
args: [postId],
});
const { write, isLoading } = useContractWrite(config);
return (
<button onClick={() => write?.()} disabled={isLoading}>
{isLoading ? 'Liking...' : '👍 Like'}
</button>
);
};
3.4 消息与通知
3.4.1 XMTP 点对点聊天
import { Client } from '@xmtp/xmtp-js';
import { ethers } from 'ethers';
export const useXMTP = () => {
const [client, setClient] = useState<Client | null>(null);
const { address, connector } = useAccount();
useEffect(() => {
if (!address) return;
const init = async () => {
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const xmtp = await Client.create(signer);
setClient(xmtp);
};
init();
}, [address]);
const sendMessage = async (recipient: string, content: string) => {
if (!client) return;
const conversation = await client.conversations.newConversation(recipient);
await conversation.send(content);
};
return { client, sendMessage };
};
3.4.2 Push Protocol 通知
import { usePush } from '@pushprotocol/restapi';
export const sendPushNotification = async (
channel: string,
title: string,
body: string,
) => {
const push = usePush();
await push.sendNotification({
channel,
title,
body,
recipient: '0xAll', // broadcast
});
};
3.5 治理与 DAO
3.5.1 Snapshot 提案
import { createProposal } from '@snapshot-labs/snapshot.js';
export const createSnapshotProposal = async ({
title,
body,
choices,
start,
end,
}: {
title: string;
body: string;
choices: string[];
start: number;
end: number;
}) => {
const proposal = await createProposal({
space: 'yourspace.eth',
type: 'single-choice',
title,
body,
choices,
start,
end,
network: '1',
snapshot: await getBlockNumber(),
});
return proposal.id;
};
3.5.2 DAO 合约(简化版)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/governance/IGovernor.sol";
interface ISocialDAO is IGovernor {
function executeProposal(uint256 proposalId) external;
}
3.6 前端 UI 示例
import { ConnectButton } from '@rainbow-me/rainbowkit';
import { useENS } from '@/hooks/useENS';
import { LikeButton } from '@/components/LikeButton';
import { useXMTP } from '@/hooks/useXMTP';
export default function Home() {
const { ensName, profile } = useENS();
const { sendMessage } = useXMTP();
return (
<main className="container mx-auto p-4">
<header className="flex justify-between items-center mb-8">
<h1 className="text-3xl font-bold">去中心化社交网络</h1>
<ConnectButton />
</header>
{ensName && (
<section className="mb-8">
<p className="text-lg">
你好,<span className="font-medium">{ensName}</span>!
</p>
{profile?.avatar && (
<img src={profile.avatar} alt="Avatar" className="w-16 h-16 rounded-full" />
)}
</section>
)}
{/* 示例帖子 */}
<article className="border rounded-lg p-4 mb-6">
<h2 className="text-xl font-semibold">我的第一篇去中心化帖子</h2>
<p className="mt-2">
这是一段示例内容,展示如何将内容存储在 IPFS 并通过 NFT
进行所有权管理。
</p>
<div className="flex items-center mt-4">
<LikeButton postId={0} />
<button
className="ml-4 text-blue-500"
onClick={() => sendMessage('0xRecipient', 'Hello!')}
>
💬 发送消息
</button>
</div>
</article>
</main>
);
}