Реалізація Token-Gated сторінок та розділів сайту
Token-gated сторінки закривають цілі розділи сайту для користувачів без потрібних токенів. Реалізація охоплює як серверну захист маршрутів, так і клієнтський UI — розмиття контенту, оверлей з закликом підключити кошелёк.
Стратегії реалізації
Hard gate — контент не завантажується взагалі без токена. Сервер повертає 403 або редирект.
Soft gate (blur gate) — контент видно розмитим, поверх наложен оверлей з закликом отримати доступ. Використовується як маркетинговий інструмент.
Progressive disclosure — частина контенту відкрита, остаток — за токеном.
Next.js: серверна захист сторінок
// app/members/page.tsx (Next.js App Router)
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { verifyTokenGate } from '@/lib/token-gate';
export default async function MembersPage() {
const cookieStore = cookies();
const token = cookieStore.get('auth_token')?.value;
if (!token) {
redirect('/connect-wallet?redirect=/members');
}
const { walletAddress } = verifyJwt(token);
const hasAccess = await verifyTokenGate(walletAddress, {
contractAddress: process.env.NFT_CONTRACT,
type: 'ERC721',
minBalance: 1
});
if (!hasAccess) {
redirect('/token-required?contract=' + process.env.NFT_CONTRACT);
}
return <MembersContent />;
}
React: Token Gate компонент
// components/TokenGate.tsx
import { useAccount, useReadContract } from 'wagmi';
import { erc721Abi } from 'viem';
interface TokenGateProps {
contractAddress: `0x${string}`;
tokenType: 'ERC721' | 'ERC20';
minBalance?: bigint;
lockedContent: React.ReactNode; // відображається при відсутності токена
children: React.ReactNode;
}
export function TokenGate({
contractAddress, tokenType, minBalance = 1n, lockedContent, children
}: TokenGateProps) {
const { address, isConnected } = useAccount();
const { data: balance, isLoading } = useReadContract({
address: contractAddress,
abi: erc721Abi,
functionName: 'balanceOf',
args: [address!],
query: { enabled: isConnected && !!address }
});
if (!isConnected) {
return <WalletConnectPrompt redirectAfter={window.location.pathname} />;
}
if (isLoading) {
return <div className="token-gate-loading">Перевірка доступу...</div>;
}
const hasAccess = (balance ?? 0n) >= minBalance;
if (!hasAccess) {
return <>{lockedContent}</>;
}
return <>{children}</>;
}
// Використання
function PremiumSection() {
return (
<TokenGate
contractAddress="0xYourNFTContract"
tokenType="ERC721"
lockedContent={
<div className="token-gate-overlay">
<h3>Тільки для власників NFT</h3>
<p>Купіть NFT для отримання доступу до екслюзивного контенту</p>
<a href="https://opensea.io/collection/your-nft">Купити на OpenSea</a>
</div>
}
>
<ExclusiveContent />
</TokenGate>
);
}
Blur-gate ефект
// Розмитий preview з оверлеем
function BlurGate({ hasAccess, children, contractAddress }) {
return (
<div className="relative">
<div className={hasAccess ? '' : 'blur-sm select-none pointer-events-none'}>
{children}
</div>
{!hasAccess && (
<div className="absolute inset-0 flex items-center justify-center bg-black/30 backdrop-blur-sm">
<div className="bg-white rounded-xl p-8 text-center shadow-xl max-w-sm">
<LockIcon className="w-12 h-12 mx-auto mb-4 text-gray-400" />
<h3 className="text-xl font-bold mb-2">Контент для членів клубу</h3>
<p className="text-gray-600 mb-4">
Отримайте NFT для доступу до цього розділу
</p>
<BuyNFTButton contractAddress={contractAddress} />
</div>
</div>
)}
</div>
);
}
Мультi-токенний доступ
// Доступ якщо є хоча б один з кількох токенів
async function checkMultiTokenAccess(walletAddress: string): Promise<{
hasAccess: boolean;
grantedBy?: string;
}> {
const gates = [
{ contract: PREMIUM_NFT, name: 'Premium NFT', type: 'ERC721' as const },
{ contract: GOVERNANCE_TOKEN, name: 'Governance Token', type: 'ERC20' as const, min: 1000n * 10n**18n }
];
for (const gate of gates) {
const has = gate.type === 'ERC721'
? await checkNFTOwnership(walletAddress, gate.contract)
: await checkERC20Balance(walletAddress, gate.contract, gate.min ?? 1n);
if (has) return { hasAccess: true, grantedBy: gate.name };
}
return { hasAccess: false };
}
Терміни
Token-gated сторінки з серверною захистом + React компонент + blur-gate — 4–6 днів.







