Разработка децентрализованной социальной сети
Децентрализованная социальная сеть — одна из наиболее технически и продуктово сложных задач в Web3. Проблемы масштабирования, censorship resistance, ownership данных, монетизация без vendor lock-in — всё это нужно решить одновременно. Lens Protocol и Farcaster предлагают разные ответы на эти вопросы.
Архитектурные подходы
Полностью on-chain (Lens Protocol)
Все социальные операции (публикации, follows, комментарии, mirrors) — транзакции на блокчейне (Polygon). Контент — хеш на IPFS, метаданные on-chain.
Преимущества: максимальная censorship resistance, interoperability (любое приложение работает с теми же данными), portable identity.
Недостатки: газ за каждое действие (даже on L2), latency блокчейна видна пользователю.
Off-chain с on-chain anchoring (Farcaster)
Сообщения хранятся на Hubs (federated servers), только identity anchored on-chain (Ethereum). Hubs синхронизируются между собой через gossip protocol.
Преимущества: быстро, без газа за каждое сообщение, scalable.
Недостатки: зависимость от Hubs (могут цензурировать), менее trustless.
ActivityPub + blockchain identity (гибрид)
ActivityPub (протокол Mastodon, Bluesky) + Ethereum аутентификация. Federated социальная сеть с крипто-кошельком как идентификатором.
Lens Protocol — on-chain social graph
Ключевые концепции
Profile NFT: каждый пользователь = ERC-721 NFT. Transferable — можно продать аккаунт с аудиторией.
Publication: пост, комментарий, mirror (репост). Хранится как transaction calldata (на IPFS контент, on-chain хеш).
Follow NFT: follower получает NFT подтверждающий подписку. Transferable — можно продать «место подписчика».
Collect: монетизация публикации — читатели платят за collect NFT (копию публикации).
Modules: кастомная логика для follow (pay to follow), collect (pay to collect, NFT-gate), reference (только follower-ы могут комментировать).
Интеграция с Lens API
import { LensClient, development, production } from "@lens-protocol/client";
const lensClient = new LensClient({
environment: production,
});
// Получение профиля
const profile = await lensClient.profile.fetch({
forHandle: "lens/stani",
});
// Публикация поста
async function createPost(
profileId: string,
content: string,
imageUrl?: string
): Promise<string> {
// Загружаем metadata на IPFS
const metadata = {
$schema: "https://json-schemas.lens.dev/publications/text-only/3.0.0/schema.json",
lens: {
id: uuidv4(),
content,
locale: "en",
mainContentFocus: "TEXT_ONLY",
tags: [],
},
};
const metadataURI = await uploadToIPFS(metadata);
// Создаём пост через Lens API
const result = await lensClient.publication.postOnchain({
contentURI: metadataURI,
});
return result.id;
}
// Follow пользователя
async function followProfile(profileId: string, followerProfileId: string) {
const result = await lensClient.follow.follow({
follow: [{ profileId }],
});
// result содержит gasless transaction через Lens Sponsored
return result;
}
Collect механика (монетизация)
// Пост с платным collect (1 WMATIC = возможность collect)
const postWithCollect = await lensClient.publication.postOnchain({
contentURI: metadataURI,
openActionModules: [
{
collectOpenAction: {
simpleCollectOpenAction: {
amount: {
currency: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", // WMATIC
value: "1",
},
recipient: creatorAddress,
referralFee: 5, // 5% идёт тому кто поделился (mirror)
followerOnly: true, // только подписчики могут collect
},
},
},
],
});
Gasless через Lens Sponsored Transactions
Lens поддерживает gasless транзакции через ERC-4337 — пользователи не платят MATIC:
const sessionClient = await lensClient.login({
onboardingUser: {
app: "0xYourAppAddress",
wallet: walletClient,
},
});
// Теперь транзакции sponsored — газ платит Lens
const result = await sessionClient.publication.postOnchain({
contentURI: metadataURI,
});
Собственная архитектура децентрализованной соцсети
Если нужна полная независимость — строим собственный протокол:
Identity
contract SocialIdentity is ERC721 {
struct Profile {
string handle; // уникальный username
string metadataURI; // IPFS CID с bio, avatar
uint256 followerCount;
uint256 followingCount;
uint256 publicationCount;
}
mapping(string => uint256) public handleToTokenId;
mapping(uint256 => Profile) public profiles;
function createProfile(string calldata handle, string calldata metadataURI)
external returns (uint256 tokenId)
{
require(handleToTokenId[handle] == 0, "Handle taken");
require(bytes(handle).length >= 3 && bytes(handle).length <= 31, "Invalid handle length");
tokenId = ++_tokenCounter;
_mint(msg.sender, tokenId);
profiles[tokenId] = Profile({
handle: handle,
metadataURI: metadataURI,
followerCount: 0,
followingCount: 0,
publicationCount: 0,
});
handleToTokenId[handle] = tokenId;
emit ProfileCreated(tokenId, handle, msg.sender);
}
}
Publications
contract Publications {
enum PublicationType { POST, COMMENT, REPOST }
struct Publication {
uint256 profileId;
string contentURI; // IPFS CID
PublicationType pubType;
uint256 parentId; // для comment/repost
uint256 timestamp;
uint256 collectCount;
uint256 commentCount;
uint256 mirrorCount;
}
mapping(uint256 => Publication) public publications;
function post(uint256 profileId, string calldata contentURI)
external returns (uint256 pubId)
{
require(socialIdentity.ownerOf(profileId) == msg.sender, "Not profile owner");
pubId = ++_publicationCounter;
publications[pubId] = Publication({
profileId: profileId,
contentURI: contentURI,
pubType: PublicationType.POST,
parentId: 0,
timestamp: block.timestamp,
collectCount: 0,
commentCount: 0,
mirrorCount: 0,
});
// Обновляем счётчик
socialIdentity.incrementPublicationCount(profileId);
emit Posted(pubId, profileId, contentURI);
}
}
Social graph (Follows)
contract SocialGraph {
// follower → set of following
mapping(uint256 => mapping(uint256 => bool)) public isFollowing;
// Follow NFT: можно продавать
mapping(uint256 => mapping(uint256 => address)) public followNFTOwner;
function follow(uint256 followerProfileId, uint256 targetProfileId) external {
require(!isFollowing[followerProfileId][targetProfileId], "Already following");
require(
socialIdentity.ownerOf(followerProfileId) == msg.sender,
"Not profile owner"
);
isFollowing[followerProfileId][targetProfileId] = true;
// Минтим Follow NFT
uint256 followNFTId = _mintFollowNFT(followerProfileId, targetProfileId);
followNFTOwner[followerProfileId][targetProfileId] = msg.sender;
socialIdentity.incrementFollowerCount(targetProfileId);
socialIdentity.incrementFollowingCount(followerProfileId);
emit Followed(followerProfileId, targetProfileId, followNFTId);
}
}
Контент и хранение
IPFS через Web3.Storage или Pinata. Публикации хранятся как JSON metadata на IPFS:
interface PublicationMetadata {
version: "2.0.0";
mainContentFocus: "TEXT_ONLY" | "IMAGE" | "VIDEO" | "ARTICLE";
content: string;
image?: string; // IPFS CID изображения
media?: MediaItem[];
locale: string;
tags: string[];
appId: string; // ваш appId
}
Ceramic/ComposeDB — decentralized database поверх IPFS для изменяемых данных (редактирование профиля, настройки).
TheGraph — индексирование on-chain событий для быстрых queries:
query GetProfileFeed($profileId: String!, $cursor: String) {
publications(
where: { author: $profileId }
orderBy: timestamp
orderDirection: desc
first: 20
after: $cursor
) {
id
contentURI
timestamp
collectCount
commentCount
comments(first: 3, orderBy: timestamp, orderDirection: desc) {
id
contentURI
author { handle }
}
}
}
Модерация в децентрализованной сети
Противоречие: censorship-resistant vs нежелательный контент. Варианты:
Labels/flags: on-chain labeling (Bluesky Labeler approach) — контент сам по себе не удаляется, но мечается. Приложения самостоятельно решают что показывать.
Client-side filtering: приложения фильтруют на уровне UI, не протокола. Censorship-resistant protocol, но модерируемые клиенты.
Community governance: DAO голосует за удаление контента из indexers. Компромисс.
Монетизация для контент-создателей
- Collect fees: читатели платят за collect (эксклюзивный контент)
- Subscription NFT: monthly membership pass → доступ к premium контенту
- Tipping: микроплатежи через контракт (или L2 для экономии газа)
- Token gating: только holders определённого NFT видят контент
- Ad revenue в токенах: протокол распределяет рекламный доход между создателями
Стек
| Компонент | Технология |
|---|---|
| Identity | Lens Protocol или кастомный ERC-721 |
| Storage | IPFS (Pinata) + Ceramic |
| Indexing | TheGraph + Lens API |
| Frontend | Next.js + wagmi + viem |
| Real-time | WebSocket (reactions, notifications) |
| L2 | Polygon (Lens native) |
Сроки
- Приложение поверх Lens Protocol (profile + feed + post + follow): 6-10 недель
- Кастомный протокол (свои контракты + full stack): 4-6 месяцев
- Модерация + governance: +4-6 недель
- Mobile приложение: +8-12 недель
- Security audit: обязателен для кастомного протокола







