A decentralized follower/following system, or on-chain social graph, moves user relationship data from centralized databases to public blockchains or decentralized storage. This architecture provides key advantages: censorship resistance, where platforms cannot unilaterally sever user connections; user sovereignty, allowing individuals to own and port their social graph; and composability, enabling any dApp to read and build upon a shared social layer. Unlike Web2 models controlled by platforms like Twitter or Instagram, a decentralized system treats the social graph as a public good, not a proprietary asset.
How to Implement a Decentralized Follower/Following System
How to Implement a Decentralized Follower/Following System
A technical guide to building a censorship-resistant social graph on-chain, covering core concepts, data structures, and implementation strategies.
The core data model typically involves two primary on-chain actions: follow and unfollow. Each action is represented as a transaction that updates a mapping or emits an event. A common Solidity pattern uses a nested mapping: mapping(address => mapping(address => bool)) public isFollowing; where isFollowing[follower][followee] returns true if a relationship exists. To make this data efficiently queryable, contracts often emit events like Followed(address indexed follower, address indexed followee) and Unfollowed(address indexed follower, address indexed followee). Indexers can then process these events to build a queryable database off-chain, as querying complex relationships directly from a smart contract is gas-intensive and slow.
Implementing the logic requires managing state updates and access control. A basic follow function checks that a user isn't following themselves and that the relationship doesn't already exist before updating the mapping and emitting an event. It's crucial to implement an unfollow function to allow relationship removal, adhering to the principle of revocable consent. For enhanced functionality, developers can integrate mechanisms like follow NFTs (ERC-721 tokens minted upon follow, as seen in Lens Protocol), signature-based follows using EIP-712 to allow gasless interactions, or staking mechanisms to deter spam. Security considerations include preventing reentrancy in follow/unfollow functions and ensuring event parameters are correctly indexed for off-chain querying.
Storing all relationship data fully on-chain, while maximally decentralized, can be prohibitively expensive due to gas costs. A hybrid approach is often more practical: store only the essential relationship actions (follow/unfollow events) on a cost-effective chain like Polygon or Base, while offloading profile data, posts, and media to decentralized storage solutions like IPFS or Arweave. The on-chain events serve as an immutable ledger of connections, and the off-chain data can be referenced via content identifiers (CIDs). This design is used by protocols like Lens Protocol and Farcaster, which maintain a canonical social graph on-chain with flexible data storage elsewhere.
To query the social graph efficiently, you need an indexing layer. Services like The Graph allow you to create a subgraph that listens to your contract's Followed and Unfollowed events. The subgraph processes these events and stores the relationships in a GraphQL-queryable database. This enables performant queries like "get all followers of address X" or "get all addresses that X follows," which are essential for building responsive social applications. Alternatively, you can run your own indexer or use services like Chainbase or Goldsky for managed indexing solutions.
When building a production system, consider key design decisions: Will you charge a fee to follow to prevent sybil attacks? Will you implement a curated follow model or allow permissionless connections? How will you handle key management and account recovery for users? Testing is critical—use frameworks like Foundry or Hardhat to simulate bulk follow transactions and measure gas costs. Ultimately, a well-architected decentralized social graph provides a foundational primitive for a new wave of interoperable, user-centric social applications, moving control from corporations to code and communities.
Prerequisites
Essential concepts and tools required to build a decentralized social graph.
Before implementing a decentralized follower/following system, you need a solid understanding of core Web3 concepts. This includes smart contracts for managing on-chain logic, decentralized storage for off-chain metadata, and cryptographic signatures for verifying user actions. You should be familiar with a blockchain development environment like Hardhat or Foundry, and have experience with a wallet provider library such as ethers.js or viem. A basic grasp of ERC standards, particularly ERC-721 for NFTs and ERC-20 for tokens, is also beneficial as they often form the basis for identity and reputation in these systems.
The core data model for a social graph involves mapping relationships between user addresses. At minimum, you'll need two primary mappings in your smart contract: one to track who a user follows (mapping(address => address[]) public following) and one to track a user's followers (mapping(address => address[]) public followers). Efficiently updating and querying these mappings is critical. Consider using structs to store additional metadata like follow timestamps, and implement events like Followed(address indexed follower, address indexed followed) to allow off-chain indexers to track relationship changes in real-time.
User identity is a foundational challenge. While you can use a raw blockchain address (e.g., 0x...) as a user ID, this is not user-friendly. Most systems integrate with an identity protocol like ENS (Ethereum Name Service) to map addresses to readable names (alice.eth). Your contract logic should resolve and display these names. Furthermore, you must decide if following is permissionless or requires consent. A unidirectional model (like Twitter) allows any address to follow another without approval, while a bidirectional model (like traditional friends lists) requires a mutual accept action, which adds complexity to your state management.
Since storing large amounts of text or profile data on-chain is prohibitively expensive, you'll need an off-chain storage solution. The standard approach is to use IPFS (InterPlanetary File System) or Arweave for permanent, decentralized storage. User profiles—containing avatars, bios, and links—are typically stored as JSON metadata files on IPFS. Your smart contract would then store the content identifier (CID) pointing to this file. Libraries like NFT.Storage or Web3.Storage can simplify this process. Your front-end application must be able to fetch and render this metadata based on the CID.
Finally, consider the economic and incentive layers. Will following be a gas-only action, or will you implement a staking mechanism to reduce spam? Some protocols use a small fee or stake that is slashed for malicious behavior. You should also plan for social graph indexing; on-chain events alone are insufficient for building a responsive application. You will likely need to use a service like The Graph to create a subgraph that indexes your contract's follow events, enabling efficient queries like "get all followers of X" without expensive on-chain calls. This completes the stack for a functional, decentralized social primitive.
How to Implement a Decentralized Follower/Following System
Designing a social graph on-chain requires efficient data structures to manage relationships between user addresses. This guide covers the essential mappings and patterns for building a scalable follower system.
At its core, a decentralized social graph maps relationships between user addresses. The most fundamental data structure is a mapping from a user to a list of addresses they follow. In Solidity, this is typically implemented as mapping(address => address[]) public following;. A reciprocal mapping, mapping(address => address[]) public followers;, tracks the inverse relationship. While storing both directions increases gas costs for writes, it drastically reduces gas costs and complexity for common read operations like displaying a profile's followers.
For scalability with large follower counts, a simple array becomes inefficient. Pagination is difficult and checking if a specific follow relationship exists (an O(n) operation) is costly. The solution is to use nested mappings for O(1) lookups. A common pattern is mapping(address => mapping(address => bool)) public isFollowing;. This acts as an adjacency matrix, where isFollowing[follower][followee] returns true if the relationship exists. You can then maintain separate arrays for following and followers lists, using the mapping as a source of truth to prevent duplicates.
Emitting events is crucial for off-chain indexers. Every successful follow and unfollow action should emit an event containing the follower and followee addresses, plus a block timestamp. This allows decentralized front-ends and subgraphs (e.g., on The Graph) to reconstruct the social graph without repeatedly querying the chain. Proper event design is a key integration point for the broader Web3 stack.
Access control and logic must be added to the basic structures. The follow function should: check the isFollowing mapping to prevent duplicate follows, update the mapping to true, push the followee to the follower's following array, push the follower to the followee's followers array, and emit a Followed event. The unfollow function performs the reverse. Consider adding a sentinel address check to prevent self-follows.
Advanced implementations may incorporate stake-based following, token-gated relationships, or delegate patterns inspired by systems like Lens Protocol. For example, you could store a uint256 stake amount in the nested mapping instead of a boolean, enabling features like curated follow lists. The core data structures—mappings for O(1) verification and arrays for enumerability—remain the foundation for these complex features.
Storage Model Comparison: On-Chain vs. Hybrid vs. Off-Chain
A technical comparison of data persistence strategies for a decentralized social graph, evaluating cost, performance, and decentralization trade-offs.
| Feature | On-Chain | Hybrid (State Roots) | Off-Chain (Indexer) |
|---|---|---|---|
Data Persistence | Immutable, permanent | Mutable state, verifiable history | Mutable, depends on service |
Gas Cost per Write | $10-50 (Ethereum L1) | $0.01-0.10 (L2) | ~$0 (service fee) |
Query Latency | ~12 sec (block time) | < 1 sec (cache) | < 100 ms |
Decentralization Guarantee | Maximum (consensus) | High (data availability) | Minimal (trusted operator) |
Developer Complexity | Low (direct calls) | Medium (proof verification) | High (orchestration) |
Data Ownership | User (via wallet) | User (via signed state) | Service provider |
Typical Use Case | Core relationships | Follower lists, preferences | Profile metadata, posts |
Event Emission for Real-Time Updates
Implement a decentralized follower system using smart contract events to enable real-time, trustless social graph updates.
In a decentralized social network, the follower/following relationship graph must be stored and updated on-chain. Unlike centralized databases, blockchain state changes are broadcast via event logs, which are a core feature of the Ethereum Virtual Machine (EVM). An event is a Solidity construct that logs data to the blockchain's transaction receipt, creating an immutable and queryable record. For a social graph, emitting an event for actions like Follow and Unfollow is more gas-efficient and scalable than storing every relationship in a public mapping, as off-chain indexers can listen to these events to reconstruct the entire network state.
The core smart contract function is straightforward. It should update an internal mapping to track relationships for on-chain logic (e.g., access control) and emit a corresponding event with the relevant addresses. Here is a basic implementation:
solidityevent Follow(address indexed follower, address indexed followed); event Unfollow(address indexed follower, address indexed followed); mapping(address => mapping(address => bool)) public isFollowing; function follow(address _toFollow) external { require(_toFollow != msg.sender, "Cannot follow self"); require(!isFollowing[msg.sender][_toFollow], "Already following"); isFollowing[msg.sender][_toFollow] = true; emit Follow(msg.sender, _toFollow); }
The indexed keyword on the address parameters allows for efficient filtering when querying past events. The internal isFollowing mapping provides instant on-chain state checks, while the event provides the historical data stream.
To consume these events in real-time, you need an off-chain indexer. Services like The Graph or direct use of a node's JSON-RPC API (eth_getLogs) can listen for Follow and Unfollow events. The indexer parses the event logs and populates a database (e.g., PostgreSQL) with the follower relationships. This decoupled architecture is key: the blockchain acts as the single source of truth for actions, while the indexer provides a fast, queryable interface for applications. Frontends can then subscribe to the indexer's GraphQL endpoint or WebSocket stream to receive instant updates when a user follows another, without polling the blockchain directly.
When designing the system, consider key trade-offs. Gas costs are incurred only for the event emission and mapping update, making it relatively cheap. Using double-indexing (both follower and followed are indexed) optimizes queries for "who does Alice follow?" and "who follows Alice?". However, be aware of blockchain limitations: event logs are not accessible from within smart contracts, and there is a limit to how much data can be emitted. For advanced features like follow NFTs or subscription tiers, you would extend the event structure to include token IDs or data fields, enabling richer social economies.
To implement a complete flow: 1) A user calls follow() via a wallet; 2) The contract updates state and emits an event; 3) An indexer (e.g., a Subgraph) detects the new event block; 4) The indexer saves the relationship to its database; 5) A frontend application queries the indexer's API and instantly reflects the new follower count and connection. This pattern, used by protocols like Lens Protocol, creates a verifiable and composable social backbone where any application can build on the same underlying graph, leveraging real-time updates powered by immutable event logs.
Gas Optimization Techniques
Building a decentralized follower/following system on-chain requires careful gas management. This guide covers key strategies to reduce costs for users and maintain protocol efficiency.
Storing data permanently on-chain is one of the most expensive EVM operations. Each new follower relationship typically requires writing to storage, which costs ~20,000 gas for a cold storage slot and ~2,900 gas for a warm slot. For a system with millions of connections, this cost scales linearly and becomes prohibitive. The primary cost drivers are:
- SSTORE operations for updating mapping or array state.
- Event emissions for indexing off-chain, which also consume gas.
- Address-to-address mapping overhead, as each unique pair is a storage entry.
Consider that a simple mapping(address => address[]) public followers; will incur high costs for both the initial push and subsequent reads if the array grows large.
Resources and Further Reading
These resources cover existing protocols, data models, and infrastructure you can reuse to implement a decentralized follower and following system without reinventing identity, storage, or indexing layers.
Frequently Asked Questions
Common technical questions and solutions for building a decentralized social graph using smart contracts and on-chain data.
A decentralized follower/following system is a social graph where user connections are stored and managed on a blockchain, rather than on a centralized server. It typically involves two core smart contracts: a registry for user profiles and a graph contract that records follow relationships as on-chain state.
When User A follows User B, a transaction is sent to the graph contract, emitting an event and updating a mapping (e.g., mapping(address => address[]) public following). This creates a permanent, censorship-resistant, and portable record. Unlike Web2 platforms, the social graph is a public good; users own their connections and can interact with them across different front-end applications ("dApps") without platform lock-in.
Conclusion and Next Steps
This guide has covered the core concepts and a practical implementation for a decentralized social graph. Here's a summary of what we built and how to extend it.
We've implemented a foundational decentralized follower/following system using smart contracts on Ethereum. The core contract manages a bidirectional mapping of relationships, allowing any address to follow another by creating an on-chain record. Key features include gas-optimized batch operations, event emission for off-chain indexing, and a permissionless design. This creates a portable social graph that is not owned by any single platform, aligning with Web3 principles of user sovereignty and data ownership.
To build a full application, you must integrate this on-chain logic with an off-chain indexer. Our contract emits Followed and Unfollowed events, which a service like The Graph or a custom indexer can process to create a queryable database. A frontend dApp would then query this indexed data via GraphQL or a similar API, displaying follower counts and connections without needing to scan the blockchain directly for every request. This hybrid architecture is standard for performant decentralized applications.
There are several advanced features to consider for a production system. Implementing follow NFTs (like Farcaster or Lens Protocol) turns relationships into transferable assets, enabling new use cases. Adding signature-based follows via EIP-712 meta-transactions would allow users to sponsor gas fees for their followers. You could also explore staking mechanisms to deter spam or integrate with decentralized identity protocols like ERC-6551 to attach the social graph to token-bound accounts.
For next steps, review and audit the security of the contract, especially the access controls on any administrative functions. Deploy the contract to a testnet like Sepolia and use a subgraph studio to index the events. Finally, explore existing standards that are emerging in this space, such as Lens Protocol's modules or the CyberConnect protocol, to understand different design trade-offs and potential for interoperability.