A Soulbound Token (SBT) is a non-transferable, non-financialized NFT that represents credentials, affiliations, or memberships. When linked together, these tokens form a social graph—a verifiable map of an identity's relationships and achievements on-chain. Unlike traditional social networks, an SBT-based graph is user-owned, portable, and composable across applications. This enables new models for decentralized identity, reputation-based governance, and trustless collaboration without centralized intermediaries.
Setting Up a Social Graph with Soulbound Tokens (SBTs)
Introduction to SBT-Based Social Graphs
Learn how to build decentralized identity and reputation systems using non-transferable tokens.
To build a social graph, you need a standard for issuing and structuring SBTs. The ERC-721 standard can be adapted by locking transfers, but dedicated standards like ERC-5192 (Minimal Soulbound NFT) provide a more explicit framework. Each SBT is minted to a user's wallet address and contains metadata defining its type—such as a course completion certificate, a DAO membership badge, or a proof of attendance. The graph emerges as applications read these tokens from a user's address to understand their verified social context.
Setting up a basic SBT involves writing a smart contract. Here's a minimal example using Solidity and the OpenZeppelin library, implementing a transfer-locked ERC-721 token:
solidity// SPDX-License-Identifier: MIT import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract SoulboundBadge is ERC721 { constructor() ERC721("SoulboundBadge", "SBT") {} // Override to make token non-transferable function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override { require(from == address(0), "Token is soulbound and non-transferable"); super._beforeTokenTransfer(from, to, tokenId); } function awardBadge(address recipient, uint256 tokenId) public { _safeMint(recipient, tokenId); } }
This contract only allows minting (from == address(0)), blocking all subsequent transfers.
The real power comes from graph composition. A user's wallet can hold SBTs from multiple issuers: a university, a protocol DAO, and a conference. A lending dApp could use this graph to offer undercollateralized loans to users with proven DAO contributor history. A governance platform could weight votes based on reputation SBTs. To query a graph, frontends use libraries like ethers.js or viem to scan a user's address for compliant token contracts and fetch the metadata, often stored on IPFS or Arweave for decentralization.
Key considerations for developers include privacy (SBTs are public by default), revocation mechanisms for issuers, and interoperability standards. Projects like Ethereum Attestation Service (EAS) and Verax provide frameworks for making on-chain attestations, a more flexible primitive similar to SBTs. When designing your system, decide if data should live fully on-chain or be referenced via a decentralized storage hash, balancing cost, permanence, and transparency for your specific use case.
Prerequisites and Setup
This guide outlines the essential tools and foundational knowledge required to build a social graph using Soulbound Tokens (SBTs).
Before writing any code, you need a solid development environment and a conceptual understanding of the components involved. The core stack for an SBT-based social graph typically includes a smart contract framework like Hardhat or Foundry, a wallet for testing (e.g., MetaMask), and a connection to a blockchain node. For this guide, we'll use the Sepolia or Goerli testnets to avoid real gas costs. You'll also need Node.js (v18 or later) and npm or yarn installed. Familiarity with TypeScript is highly recommended for type safety when interacting with contracts and the Ethers.js or Viem libraries.
Soulbound Tokens (SBTs) are non-transferable NFTs that represent credentials, affiliations, or memberships. Unlike standard ERC-721 tokens, SBTs are permanently bound to a wallet address, making them ideal for constructing a persistent, user-centric social graph. The most common implementation standard is ERC-5192, which defines a minimal interface for non-transferable NFTs. Your social graph logic will be built on top of this foundation, using SBT mints to create edges (connections) between addresses (nodes). For example, issuing an SBT from DAO_A to user_wallet creates a verifiable "member of" link.
You must decide on the initial architecture of your graph. Will it be a single, monolithic contract that mints all SBTs, or a factory pattern where each community deploys its own SBT contract? A common approach is to use a registry contract that maintains a mapping of authorized issuers and the SBT contracts they've deployed. This separates the issuance logic from the graph's discovery mechanisms. Also, consider if your SBTs will have revocable bindings (using a burn function controlled by the issuer) or be permanently soulbound, as this affects trust assumptions and user experience.
For local development and testing, set up a Hardhat project. Run npx hardhat init and select the TypeScript template. Install necessary packages: npm install @openzeppelin/contracts dotenv @nomicfoundation/hardhat-toolbox. Configure your hardhat.config.ts to use the Sepolia testnet by adding an RPC URL from a provider like Alchemy or Infura and funding a test wallet with Sepolia ETH from a faucet. Store your private key in a .env file using process.env. This setup allows you to compile, deploy, and test your contracts locally before moving to a testnet.
Finally, plan your data indexing strategy. On-chain events emitted during SBT minting (e.g., Transfer with a from address of 0x0) are the raw data of your social graph. You will likely need an off-chain indexer to query this data efficiently. You can use The Graph subgraph to index these events into a queryable GraphQL API, or use a service like Alchemy's Enhanced APIs for NFT data. This indexed data layer is crucial for building a responsive application that can quickly answer questions like "Which SBTs does this address hold?" or "Which addresses hold this specific SBT?"
Setting Up a Social Graph with Soulbound Tokens (SBTs)
This guide explains how Soulbound Tokens (SBTs) create a decentralized social graph, detailing the technical architecture and providing a practical implementation example.
A social graph maps relationships and affiliations between entities. In Web3, Soulbound Tokens (SBTs) are non-transferable, non-financialized tokens that act as verifiable credentials, issued by a "soul" (a wallet) to represent commitments, memberships, or achievements. When SBTs are issued and received between wallets, they form a decentralized social graph. This graph is not owned by a central platform but is composed of publicly verifiable attestations on-chain, enabling applications to query relationships and reputations without intermediaries.
The technical architecture for an SBT-based social graph involves three core components: the SBT Smart Contract, an Indexer or Subgraph, and a Query Interface. The contract, typically following the ERC-721 or ERC-1155 standard with transfer locking, mints tokens to recipient addresses. An indexer (like The Graph) listens for contract events (Transfer, Attestation) to build a searchable database of issuers, holders, and token types. Finally, a front-end or API queries this indexed data to visualize connections, such as showing all members of a DAO or an individual's skill certifications.
Here is a simplified example of an SBT contract snippet using Solidity and OpenZeppelin, which prevents transfers after minting:
solidity// SPDX-License-Identifier: MIT import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract SoulboundToken is ERC721 { address public issuer; constructor(string memory name, string memory symbol) ERC721(name, symbol) { issuer = msg.sender; } function mintSBT(address to, uint256 tokenId) external { require(msg.sender == issuer, "Only issuer can mint"); _safeMint(to, tokenId); } // Override to make token non-transferable function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) internal virtual override { require(from == address(0) || to == address(0), "SBT: Non-transferable"); super._beforeTokenTransfer(from, to, tokenId, batchSize); } }
This ensures tokens are soulbound, only mintable and burnable, not tradable.
To build the graph, you must index the minting events. Using The Graph, you define a schema that captures the Soul (holder address) and the Token (with a type property). The mapping script processes Transfer events where from is the zero address (mints). The resulting subgraph allows queries like fetching all tokens held by an address or all holders of a specific token type, effectively traversing the social graph. Projects like Gitcoin Passport and Orange Protocol use similar architectures to aggregate on-chain and off-chain credentials.
Practical use cases for SBT social graphs are extensive. They enable sybil-resistant governance by proving unique personhood, credential-based access control for gated communities or content, and reputational systems for decentralized lending or hiring. A developer could, for instance, query the graph to find all wallets holding both a specific DAO membership SBT and a "Solidity Expert" credential SBT to target a governance proposal. The composability of this data across applications, without silos, is a foundational shift from Web2 social networks.
When implementing, consider key challenges: privacy for sensitive attestations (solved with zero-knowledge proofs or private data availability), revocation mechanisms for expired credentials, and discovery standards for interoperability. Protocols like EIP-4973 (Account-bound Tokens) and EAS (Ethereum Attestation Service) provide community-approved frameworks. The goal is a user-owned, portable identity layer where social capital is built on verifiable actions, not platform-dependent profiles.
Essential Resources and Tools
These tools and standards are commonly used to build social graphs with Soulbound Tokens (SBTs). Each resource focuses on a specific layer: token standards, attestations, identity aggregation, and offchain data needed to make social relationships queryable and composable.
Step 1: Implementing the SBT Smart Contract
This guide walks through building the foundational smart contract for a social graph using Soulbound Tokens (SBTs) on Ethereum, focusing on non-transferability and social attestations.
A Soulbound Token (SBT) is a non-transferable NFT that represents a credential, affiliation, or membership. Unlike standard ERC-721 tokens, SBTs are permanently bound to a single wallet address, making them ideal for building a persistent, user-centric social graph. For this implementation, we'll extend the OpenZeppelin library's ERC721 contract, overriding the critical transfer functions to enforce non-transferability. This creates the immutable link between identity and attestation that underpins the graph.
The core of the SBT contract logic involves disabling all token transfer mechanisms. We override _beforeTokenTransfer from the ERC-721 standard, which is called before any mint, burn, or transfer. Our implementation will permit only two actions: initial minting (from the zero address) and burning. Any attempt to transfer the token between two user-owned addresses will be reverted. Here's the essential function:
solidityfunction _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) internal virtual override { require(from == address(0) || to == address(0), "SBT: non-transferable"); super._beforeTokenTransfer(from, to, tokenId, batchSize); }
To populate the social graph, we need a mechanism for issuing attestations. We'll add a mint function that allows a designated issuer (or the token owner for self-attestations) to create a new SBT. Each token should encode the attestation data. A robust approach is to store a structured metadata URI for each token ID, which points to a JSON file containing details like the issuer, issuance date, and credential type. This keeps on-chain gas costs low while maintaining a rich, verifiable data layer.
For a functional social graph, we must also consider revocation. While SBTs are non-transferable, there are valid reasons for an issuer to revoke a credential. We can implement a burn function that allows the original issuer (or a governing contract) to destroy a token they created. It's crucial to emit a standardized event like AttestationRevoked during burning so that indexers and front-end applications can update the graph state accordingly, removing invalidated connections.
Finally, deploy and verify your contract on a testnet like Sepolia or Goerli. Use the OpenZeppelin Upgrades Plugin if you anticipate needing future upgrades to the attestation logic. Remember that the permanence of the social graph depends on the contract's address, so initial design choices are critical. Once deployed, this contract serves as the single source of truth for all attestations within your application's ecosystem.
Designing SBT Schemas and Revocation Logic
This section details the core design decisions for your social graph's data structure and token lifecycle management.
A Soulbound Token (SBT) schema defines the metadata and attributes permanently linked to a user's wallet. Unlike fungible tokens, SBTs are non-transferable and represent persistent credentials or affiliations. For a social graph, your schema must encode relationship data. Common attributes include issuer (the attesting entity), recipient (the soul receiving the token), relationshipType (e.g., follow, endorse, member), and a timestamp. You can extend this with custom fields like skillLevel for endorsements or communityRole for DAO memberships. The schema is typically defined using standards like EIP-4973 for account-bound tokens or implemented within broader frameworks.
While SBTs are designed to be non-transferable, their state must be manageable. Revocation logic is critical for maintaining graph integrity when a relationship ends or an attestation is invalidated. There are two primary models: burn revocation and stateful revocation. Burning an SBT destroys it entirely, providing a clear on-chain signal but erasing historical context. Stateful revocation involves updating a token's metadata (e.g., setting an isRevoked flag to true) while keeping the token in the wallet. This preserves a record of the revoked attestation, which can be important for audit trails and sybil resistance mechanisms.
Your choice of revocation model dictates the smart contract architecture. A burnable SBT contract would inherit from OpenZeppelin's ERC721Burnable. A stateful model requires custom logic, such as an onlyIssuer function that toggles a revocation flag and emits a Revoked event. It's essential to design access control carefully; often, only the original issuer (or a designated admin) should be able to revoke. Consider gas costs and state bloat—burning is a final, one-time cost, while stateful checks require reading storage every time the token's status is verified.
For a functional social graph, your contracts must also handle querying and traversal. While the blockchain stores the raw issuance and revocation events, an off-chain indexer (like The Graph) is typically needed to efficiently answer questions like "Who follows this address?" or "List all active endorsements for this soul." Your schema and event structure (e.g., Issued(address indexed issuer, address indexed recipient, uint256 tokenId) and Revoked(uint256 tokenId)) will form the basis of these subgraphs. Plan your data structures with these queries in mind to ensure your graph is usable.
Step 3: Indexing and Querying the Social Graph
After deploying Soulbound Tokens (SBTs) to represent social connections, the next step is to make that on-chain data accessible and useful. This guide covers indexing the raw blockchain data into a structured graph and building queries to analyze relationships.
Raw on-chain data from SBT mints and transfers is not optimized for complex social analysis. An indexing service like The Graph or Subsquid is essential. These services listen for events from your SBT contracts, process them, and store the data in a queryable database (e.g., PostgreSQL). For a social graph, you would index events like AttestationCreated (minting an SBT link) and Transfer (for revocable SBTs), mapping them to entities like Wallet, SBT, and Connection. This transforms a list of transactions into a network of nodes and edges.
With the data indexed, you use GraphQL to query it. Unlike SQL, GraphQL lets you request nested, related data in a single query, which is ideal for graph structures. A query to find all connections for a given wallet might look like this:
graphqlquery GetSocialConnections($address: String!) { wallet(id: $address) { attestationsMade { id recipient schema { name } } attestationsReceived { id attester } } }
This returns a complete set of a user's social attestations as both issuer and recipient.
Effective queries power the application layer. Key analyses include calculating social proximity (degrees of separation between two wallets by traversing SBT links), identifying communities using clustering algorithms on the connection graph, and measuring reputation scores based on the density and type of attestations a wallet holds. For performance, consider pre-computing common metrics in the indexer subgraph and exposing them as derived fields. Always paginate queries for wallets with hundreds of connections to avoid timeouts.
For developers, the primary tools are The Graph's Subgraph Studio or Subsquid's Squid SDK. You define your schema (schema.graphql), write the indexing logic (a mapping in AssemblyScript or TypeScript) to handle blockchain events, and deploy the subgraph. The hosted service then provides a GraphQL endpoint. For decentralized data, you can deploy a subgraph to The Graph's decentralized network, where Indexers stake GRT to serve queries.
When designing your schema, model relationships bidirectionally for efficient traversal. For example, a Wallet entity should have fields like attestationsMade and attestationsReceived, both linking to an Attestation entity. This avoids expensive join operations at query time. Use derived fields for computed values; a wallet's connectionCount can be stored as an integer that updates on each new attestation, making queries for profile pages extremely fast.
Finally, integrate the GraphQL endpoint into your frontend or backend. Use clients like Apollo or urql to manage queries, caching, and state. For example, after a user connects their wallet, your dApp can fetch their social graph data to personalize the UI, display their network, or gate access to features based on their attested relationships. This seamless flow—from on-chain action to indexed data to application logic—is what makes SBT-based social graphs dynamic and functional.
Integrating SBTs with Application Permissions
This guide explains how to use Soulbound Tokens (SBTs) as a permission layer for on-chain applications, moving beyond simple identity verification to enable granular access control.
Soulbound Tokens (SBTs) are non-transferable NFTs that represent credentials, affiliations, or achievements. When integrated with application logic, they function as a powerful permissioning system. Instead of a simple wallet check, your smart contract can verify if a user holds a specific SBT—like a membership token, a proof-of-attendance, or a verified skill credential—before granting access to a function. This creates a social graph where access rights are derived from a user's verifiable, on-chain reputation and relationships, not just their token balance.
The core technical pattern involves adding a modifier or a require statement to your contract's functions. For example, a gated Discord server might require an SBT from a specific DAO. A lending protocol could offer better rates to users holding a "Trusted Borrower" SBT minted by a credit guild. Here's a basic Solidity example using the ERC-721 balanceOf check for an SBT contract:
solidityfunction accessGatedFeature() external { require(sbtContract.balanceOf(msg.sender) > 0, "SBT required"); // Proceed with privileged logic }
For more complex rules, you can check for specific token IDs or use enumerable extensions to verify possession of tokens with certain traits.
Effective permission design requires mapping application features to specific SBT criteria. Consider implementing tiered access: a common SBT grants basic features, while a rarer, earned SBT unlocks advanced tools. It's also crucial to plan for SBT revocation. Since SBTs are non-transferable but can be burned by the issuer, your application logic should handle cases where a user's permissions are revoked. Always query the live on-chain state; do not cache SBT ownership data off-chain for critical permissions to ensure security and real-time accuracy.
Comparison of SBT Implementation Standards
Key differences between major standards for implementing non-transferable, soulbound tokens on EVM-compatible chains.
| Feature / Metric | ERC-721 (Modified) | ERC-5192 (Minimal) | ERC-5484 (Consensys) |
|---|---|---|---|
Standard Status | Community Fork | Final EIP | Draft EIP |
Core Locking Mechanism | Custom |
| Authorization & Burn Guard |
Default Transfer Behavior | Reverts all transfers | Reverts all transfers | Allows authorized burns only |
On-Chain Soul Metadata | |||
Gas Cost for Mint (est.) | ~85k gas | ~52k gas | ~120k gas |
Primary Use Case | Custom SBT logic | Simple, universal locking | Reputation & attestations |
Audit Status | Uses audited base | Formally verified | In review |
Adoption Examples | Gitcoin Passport (legacy) | Sismo Badges, Layer3 | Not yet live on mainnet |
Frequently Asked Questions (FAQ)
Common technical questions and solutions for developers building with Soulbound Tokens (SBTs) and social graphs.
A Soulbound Token (SBT) is a non-transferable, non-fungible token (NFT) permanently bound to a single wallet address, often called a "Soul." Unlike regular NFTs, SBTs cannot be sold or transferred after minting. This immutability is enforced at the smart contract level, typically by overriding or restricting the transferFrom and safeTransferFrom functions defined in the ERC-721 or ERC-1155 standards.
Key Technical Differences:
- Transfer Logic: Standard NFTs use
_transferinternally; SBTs revert on any transfer call. - Burnability: Some implementations allow the original issuer or the Soul itself to burn the token, providing an escape hatch.
- Use Case: While NFTs represent ownership of assets, SBTs represent verifiable credentials, memberships, or achievements that are intrinsic to an identity.
Conclusion and Next Steps
You have now implemented the core components of a social graph using Soulbound Tokens (SBTs). This final section summarizes the key concepts and outlines practical next steps for developers.
This guide demonstrated how to construct a decentralized social graph using Soulbound Tokens (SBTs). We covered the foundational concepts: SBTs as non-transferable, on-chain attestations of identity, relationships, and credentials. You learned to implement key contracts for minting SBTs, establishing connections between wallets, and querying the resulting graph structure. The power of this model lies in its composability—each SBT acts as a verifiable data point that other applications can trust and build upon without centralized intermediaries.
To move from a prototype to a production-ready system, consider these next steps. First, enhance data privacy and scalability. Explore zero-knowledge proofs (ZKPs) with libraries like Circom or Halo2 to attest to relationships or credentials without revealing underlying details on-chain. Second, integrate with existing identity standards like Verifiable Credentials (VCs) or Ethereum Attestation Service (EAS) to improve interoperability and leverage established verification tooling.
Finally, consider the application layer. Your social graph SBTs can power various use cases: - Sybil-resistant governance: Weight voting power based on proven reputation or membership SBTs. - On-chain resume: Allow users to aggregate skill and employment credentials from multiple issuers. - Exclusive communities: Gate access to token-gated channels in platforms like Guild.xyz or Collab.Land based on specific SBT holdings. The next phase is to define clear utility that drives user adoption and issuance of these tokens.
As you build, prioritize user experience and security. Abstract away blockchain complexity with account abstraction (ERC-4337) for gas sponsorship and batch transactions. Implement rigorous access controls and revocation mechanisms for your SBT contracts. Monitor the evolving landscape of ERC-4973 (the proposed standard for SBTs) and related EIPs to ensure future compatibility. The goal is to create a system that is not only technically robust but also intuitive and valuable for end-users.
For further learning, explore these resources: the Soulbound Tokens Wiki for technical specifications, the Ceramic Network for decentralized data composability, and the Lens Protocol to study an existing social graph implementation. Start small, iterate based on user feedback, and contribute to building a more verifiable and user-centric web3 social layer.