NFT-Gated Content System Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
NFT-Gated Content System Development
Medium
~3-5 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1051
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    827
  • image_logo-aider_0.jpg
    AIDER company logo development
    762
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    850

NFT-Gated Content System Development

Typical mistake when implementing NFT-gated access — check ownership only on frontend. User connects wallet, JS does ownerOf(tokenId), gets address, compares with wallet account — that's it, access granted. Problem: trivially bypass this from DevTools. All gating must happen on backend; frontend only initiates flow.

Proper Ownership Verification

Message Signature Scheme (Sign-In With Ethereum)

Standard EIP-4361 (SIWE) is the right path. User signs standardized message with private key, backend verifies signature and checks ownership contract.

Scheme:

  1. Frontend requests nonce from backend for address (replay attack protection)
  2. Forms SIWE message — standard text with domain, address, nonce, timestamp, expiry
  3. User signs via wallet (personal_sign)
  4. Backend verifies signature: recovers address from signature via ecrecover, checks nonce, checks timestamp, checks ownerOf / balanceOf on contract
// Backend verification (Node.js)
import { SiweMessage } from "siwe"
import { createPublicClient, http } from "viem"

async function verifyNFTAccess(message: string, signature: string, contractAddress: string) {
  const siweMessage = new SiweMessage(message)
  const { success, data } = await siweMessage.verify({ signature })

  if (!success) throw new Error("Invalid signature")
  if (data.nonce !== await getNonce(data.address)) throw new Error("Invalid nonce")
  if (new Date(data.expirationTime!) < new Date()) throw new Error("Expired")

  // Check NFT ownership on-chain
  const client = createPublicClient({ chain: mainnet, transport: http(RPC_URL) })
  const balance = await client.readContract({
    address: contractAddress,
    abi: ERC721_ABI,
    functionName: "balanceOf",
    args: [data.address as `0x${string}`]
  })

  if (balance === 0n) throw new Error("No NFT found")

  // Issue JWT session token
  return issueJWT(data.address)
}

After successful verification — JWT token with short TTL (e.g., 24 hours). Repeated ownership verification on every request not needed — check JWT, recheck ownership on token refresh.

Granular Access: Specific Token vs. Any from Collection

Two modes:

Collection-level gating: any collection token holder gets access. Check balanceOf(address) > 0. Fast, cheap on RPC calls.

Token-specific gating: access only for specific tokenId holder (e.g., NFT ticket for event). Check ownerOf(tokenId) == address. Need to store tokenId → resource mapping.

Trait-based gating: access only for NFTs with specific attributes (rare traits, certain level). Either on-chain attribute recording or verified mapping with IPFS metadata. Most complex — need to pre-index collection metadata.

ERC-1155: Multi-token Gating

ERC-1155 opens more flexible models. balanceOf(address, tokenId) returns specific token ID count. Can build tier-based access:

  • tokenId 1 = basic access
  • tokenId 2 = premium access
  • tokenId 3 = VIP access

Or: require 10 tokens of tokenId 1 for specific action (gamification). More complex logic, but verified same way with single balanceOf call.

Infrastructure for Scalable Gating

Caching Ownership Data

With active audience of 10k+ users, checking ownerOf on every request stresses RPC. Solution: cache with TTL.

User authenticates → check ownership → cache result (TTL: 5 min) → issue JWT (TTL: 24h)
Every API request: check JWT (locally, fast)
JWT refresh: check cache, on miss go to RPC

On NFT transfer cache stales in 5 minutes — acceptable for most scenarios. For immediate reaction on transfer — subscribe to Transfer events via WebSocket and invalidate cache.

Monitoring Transfer Events for Access Revocation

Selling NFT should immediately revoke seller's access — critical for paid communities. Subscribe to Transfer event via Alchemy/Infura WebSocket:

const filter = {
  address: NFT_CONTRACT,
  topics: [
    ethers.id("Transfer(address,address,uint256)"),
    null,  // from: any
    null   // to: any
  ]
}

provider.on(filter, (log) => {
  const [from, to, tokenId] = parseTransferEvent(log)
  revokeAccess(from)    // invalidate session for previous owner
  grantAccess(to)       // pre-cache for new owner
})

Multichain Gating

Collection may be on Ethereum, users want to pay gas on Polygon — common for bridged collections. Multichain gating: check ownership on multiple chains, one match sufficient.

Moralis or Alchemy NFT API simplify — single call with multiple chains:

const nfts = await alchemy.nft.getNftsForOwner(address, {
  contractAddresses: [CONTRACT_ETH, CONTRACT_POLYGON],
})
const hasAccess = nfts.ownedNfts.length > 0

Content: What and How to Protect

Static content (PDF, video, images): files in S3/R2 with private access. Backend generates signed URL with short TTL (15-60 minutes) only for verified holders. Direct file links don't leak.

Dynamic content (pages, API data): middleware checks JWT token before every response. Without valid JWT — 401.

Streaming content (live video): auth via token in query param or header on stream connect. After connect — periodic revalidation every N minutes.

Development Timeline

SIWE integration with basic collection-level gating and JWT sessions — 2 days. Adding Transfer event watcher and automatic access revocation — 1 day more. Trait-based gating with metadata indexing — 1-2 days depending on collection size. Multichain support — 1-2 days more.

Full system with monitoring, caching, and multichain verification — 4-5 working days.