Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

Launching a Non-Transferable Reputation Token (Soulbound)

A step-by-step technical guide for developers to implement a soulbound token contract for on-chain reputation using standards like ERC-721S, including minting logic, revocation, and public interfaces.
Chainscore © 2026
introduction
ON-CHAIN IDENTITY

Introduction to Soulbound Reputation Tokens

Soulbound tokens (SBTs) are non-transferable digital assets that represent credentials, affiliations, or reputation on the blockchain. This guide explains their core concepts and how to launch one.

A soulbound token (SBT) is a non-transferable, non-fungible token (NFT) permanently bound to a specific wallet address, often called a "Soul." Unlike standard NFTs, SBTs cannot be sold or transferred, making them ideal for representing immutable on-chain identity attributes. The concept, popularized by Vitalik Buterin, Glen Weyl, and Puja Ohlhaver in their 2022 paper, aims to create a decentralized society (DeSoc) where identity and reputation are verifiable components of the Web3 stack. Key properties include permanence, social utility, and composability with other smart contracts.

The primary use cases for SBTs are extensive. They can represent educational degrees, professional certifications, DAO voting history, event attendance proofs, credit scores, or loan repayment records. For instance, a developer could hold an SBT proving they completed a specific smart contract audit course, which a DAO could then use to gatekeeper access to high-security tasks. This creates a portable, user-centric identity that is not controlled by a central issuer after minting, enabling new forms of governance, underwriting, and community building.

From a technical perspective, an SBT is typically an ERC-721 or ERC-1155 token with a modified transfer function. The core implementation involves overriding the transferFrom and safeTransferFrom functions to revert all transfer attempts, effectively locking the token to the minting address. Some implementations, like the ERC-5484 standard for "Soulbound Tokens," provide a formalized interface for this behavior. It's crucial that the non-transferability is enforced at the smart contract level, not just by social convention, to ensure the token's integrity and trustlessness.

Launching a basic SBT involves writing and deploying a smart contract. Below is a simplified example using Solidity and the OpenZeppelin library, which creates a non-transferable ERC-721 token. The key is the _beforeTokenTransfer hook, which blocks all transfers after the initial mint.

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract SoulboundReputation is ERC721 {
    constructor(string memory name, string memory symbol) ERC721(name, symbol) {}

    // Mint a new reputation token to an address
    function awardToken(address to, uint256 tokenId) public {
        _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), "Token is soulbound and non-transferable");
        super._beforeTokenTransfer(from, to, tokenId, batchSize);
    }
}

When designing a reputation system with SBTs, consider critical factors like revocability, privacy, and sybil resistance. Some use cases may require an issuer to revoke a credential; this can be implemented with a burn function restricted to the issuer. For privacy, consider storing metadata off-chain (e.g., on IPFS or using zero-knowledge proofs) to avoid exposing sensitive data on-chain. To prevent sybil attacks—where a user creates multiple wallets—systems often require a form of proof-of-personhood or linkage to a verified identity as a prerequisite for receiving an SBT.

The ecosystem for SBTs and decentralized identity is rapidly evolving. Key projects building infrastructure include Ethereum Attestation Service (EAS) for on/off-chain attestations, Gitcoin Passport for aggregating web2 and web3 credentials, and Worldcoin for global proof-of-personhood. As a developer, you can integrate these primitives to build sophisticated reputation-based applications. The long-term vision is an interoperable web where your contributions across DAOs, platforms, and games compose into a persistent, user-owned reputation graph, fundamentally changing how trust is established online.

prerequisites
SOULBOUND TOKEN TUTORIAL

Prerequisites and Setup

This guide outlines the essential tools and knowledge required to deploy a non-transferable reputation token, commonly known as a Soulbound Token (SBT), on the Ethereum blockchain.

Before writing any code, you must set up a foundational development environment. This requires a basic understanding of Ethereum, smart contracts, and the Solidity programming language. You will need Node.js (v18 or later) and npm installed to manage dependencies. The primary tools for this tutorial are Hardhat, a popular Ethereum development framework, and OpenZeppelin Contracts, a library of secure, audited smart contract components. You can initialize a new Hardhat project by running npx hardhat init in your terminal.

The core logic for a Soulbound Token is built by extending the ERC-721 standard for non-fungible tokens (NFTs). However, we must override the token transfer functions to make them non-transferable. We will use OpenZeppelin's implementations as a secure starting point. Key contract functions to understand are _beforeTokenTransfer and _approve. By overriding these, we can enforce the soulbound property, reverting any transaction that attempts to transfer the token after its initial mint to a user.

For local testing and deployment, you will need access to an Ethereum node. You can use Hardhat's built-in network for development. To deploy to a testnet or mainnet, you will need a wallet with test ETH (e.g., from a Goerli faucet) and an RPC provider URL from a service like Alchemy or Infura. Securely manage your private keys or mnemonic phrase using environment variables (e.g., a .env file) with the dotenv package. Never commit sensitive keys to version control.

key-concepts-text
SOULBOUND TOKENS

Key Concepts: ERC-721S and Non-Transferability

ERC-721S is a standard for creating non-transferable, 'soulbound' tokens on Ethereum, designed to represent persistent on-chain identity, credentials, and reputation.

The ERC-721S standard extends the popular ERC-721 (NFT) specification by introducing a core restriction: tokens cannot be transferred between wallets after the initial mint. This property, often called soulbinding, makes these tokens ideal for representing immutable on-chain attributes. Unlike standard NFTs, which are assets, ERC-721S tokens are non-financialized identifiers. They can represent achievements, memberships, voting rights, or attestations that should be permanently linked to a specific Ethereum address. The standard is defined by a simple but powerful rule: the transferFrom and safeTransferFrom functions must revert, preventing any secondary market or gifting.

Implementing an ERC-721S contract is straightforward for developers familiar with OpenZeppelin's ERC-721. The key is to override the transfer functions to always fail. Here's a minimal Solidity example:

solidity
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract SoulboundToken is ERC721 {
    constructor() ERC721("SoulboundBadge", "SBT") {}
    function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) internal virtual override {
        require(from == address(0) || to == address(0), "SoulboundToken: Non-transferable");
        super._beforeTokenTransfer(from, to, tokenId, batchSize);
    }
}

This hook allows minting (from == address(0)) and burning (to == address(0)), but blocks all other transfers. This enforces the soulbound property at the protocol level.

The primary use case for ERC-721S is building on-chain reputation systems. A decentralized autonomous organization (DAO) could issue non-transferable governance tokens to verified members, ensuring one-person-one-vote. A gaming protocol could award soulbound achievement badges for in-game accomplishments. An educational platform could issue verifiable, non-sellable course completion certificates. By removing transferability, these tokens gain trust and integrity; their presence in a wallet becomes a reliable signal of identity or history, not just purchasing power. This prevents Sybil attacks and reputation farming that plague transferable token-based systems.

When designing a soulbound token system, consider mint permissions and revocation. Will tokens be minted by a central authority, via a claim function, or through automated on-chain logic? The _beforeTokenTransfer hook also allows for burning, which enables revocation of badges or memberships if needed. Furthermore, consider metadata permanence. Using a decentralized storage solution like IPFS or Arweave for token metadata (images, description) ensures the token's meaning is as immutable as its on-chain ownership. Tools like OpenSea's 'Soulbound' trait can help marketplaces properly display these non-transferable assets.

Adoption of ERC-721S and similar standards is growing within the Decentralized Society (DeSoc) and Proof-of-Personhood ecosystems. Projects like Ethereum Attestation Service (EAS) use similar immutable attestation models. However, developers must also consider user experience challenges, such as the permanent loss of tokens if a private key is compromised. Proposals for social recovery or guardian-based burning are active areas of development to address this. For builders, starting with ERC-721S provides a simple, audited foundation for creating persistent, meaningful on-chain identity primitives.

IMPLEMENTATION ANALYSIS

Comparing Soulbound Token Standards

A technical comparison of the leading standards for creating non-transferable tokens, focusing on developer experience, security, and ecosystem support.

Feature / MetricERC-721S (Proposed)ERC-5192 (Minimal)SBTs via Custom Logic

Standard Status

Draft EIP

Final (EIP-5192)

Custom Implementation

Core Locking Mechanism

Explicit locked state variable

Minimal interface (locked())

Custom modifiers or registry

Gas Cost for Mint (est.)

~85k gas

~65k gas

~70k - 120k+ gas

Revocation / Burn Support

Batch Operations

Primary Use Case

Reputation, credentials, memberships

Minimal soulbound proof-of-concept

Highly specialized governance or rewards

Audit & Mainnet Usage

Limited testnet deployments

Used by Galxe, Orange Protocol

Varies by project; higher audit burden

EVM Wallet Compatibility

Requires wallet updates for UI

Basic support via metadata

May not be recognized as non-transferable

contract-implementation
SOULBOUND TOKEN DEVELOPMENT

Step 1: Implementing the Core Contract

This guide walks through creating the foundational smart contract for a non-transferable reputation token using the ERC-721 standard.

A non-transferable token (Soulbound Token or SBT) is a digital asset permanently bound to a single wallet address. Unlike standard NFTs, it cannot be sold or transferred, making it ideal for representing immutable credentials, achievements, or reputation. The most common approach is to modify the ERC-721 standard, overriding its transfer functions to prevent movement after the initial mint. This creates a permanent, on-chain record of ownership that is publicly verifiable and resistant to sybil attacks.

The core of the contract involves inheriting from a standard like OpenZeppelin's ERC721 and implementing a custom _beforeTokenTransfer hook. This internal function is called before any token transfer, including minting and burning. By overriding it, you can enforce transfer restrictions. A typical implementation checks if the from address is the zero address (indicating a mint) and allows the transaction; otherwise, it reverts. This simple logic is the foundation of the token's non-transferable property.

Here is a basic Solidity implementation using OpenZeppelin v5.0:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract SoulboundReputation is ERC721 {
    constructor(string memory name, string memory symbol) ERC721(name, symbol) {}

    function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize)
        internal
        virtual
        override
    {
        require(from == address(0), "Soulbound: Token is non-transferable");
        super._beforeTokenTransfer(from, to, tokenId, batchSize);
    }

    function safeMint(address to, uint256 tokenId) public {
        _safeMint(to, tokenId);
    }
}

This contract allows minting via safeMint but will revert on any subsequent transfer attempt.

Beyond the basic lock, consider additional features for a robust reputation system. You may want to implement an allowlist for controlled minting, ensuring only authorized entities can issue tokens. Adding metadata that points to an off-chain JSON file (using the tokenURI function) allows you to attach detailed credential information. For revocation scenarios—necessary if a credential becomes invalid—you can implement a burn function restricted to an admin or the original issuer, providing a controlled exit mechanism without enabling transfers.

Before deployment, thoroughly test the contract's behavior. Use a framework like Hardhat or Foundry to write tests that verify: minting succeeds, standard transfers fail, and burning (if implemented) works as intended. Pay special attention to edge cases, such as interactions with marketplaces that may attempt to call transfer functions. Deploy the verified contract to a testnet first. The final step is to integrate this contract with a frontend dApp or backend service that handles the logic for issuing tokens to users based on specific reputation criteria.

minting-logic
CORE CONTRACT LOGIC

Step 2: Designing Minting and Burning Logic

This step defines the rules for how your reputation token is issued and revoked, ensuring it remains non-transferable and tied to specific on-chain actions or attestations.

The minting and burning logic is the core business logic of your Soulbound Token (SBT) contract. Unlike a standard ERC-20, you cannot allow arbitrary transfers. The primary design goals are to prevent token transfers between wallets and to control issuance and revocation through authorized logic. This is typically achieved by overriding the _update function from the ERC-721 or ERC-1155 standard to block all transfers except for minting (from the zero address) and burning (to the zero address).

A common pattern is to implement role-based access control using a system like OpenZeppelin's AccessControl. You would designate a MINTER_ROLE and a BURNER_ROLE. The mint function should be callable only by an address with the minter role and should include logic to bind the token to a specific recipient, often based on verifiable criteria. For example, you might mint an SBT to a user who has completed a governance proposal, achieved a certain on-chain score from a verifier contract, or holds a specific NFT.

The burning logic must be equally controlled. A token should only be burnable by the contract owner or a designated burner role, typically to revoke reputation due to malicious behavior, expired credentials, or a user's request to leave a system. It is crucial that the token owner cannot transfer or burn their own SBT, as this would break the soulbound property. Your burn function must enforce this permission check.

Here is a simplified code snippet demonstrating the key overrides and a mint function for an ERC-721 SBT:

solidity
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";

contract ReputationToken is ERC721, AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor() ERC721("Reputation", "REP") {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    }

    // Override to block all transfers except mint/burn
    function _update(
        address to,
        address from,
        uint256 tokenId
    ) internal virtual override {
        require(
            from == address(0) || to == address(0),
            "SoulboundToken: Transfers are disabled"
        );
        super._update(to, from, tokenId);
    }

    // Permissioned mint function
    function mintReputation(address to, uint256 tokenId) external onlyRole(MINTER_ROLE) {
        _safeMint(to, tokenId);
    }
}

Consider more advanced logic for dynamic systems. Your minting function could accept off-chain attestations signed by a trusted issuer, verified via ECDSA recovery in the contract. Alternatively, it could query an external oracle or verifier contract to check if minting conditions are met. The burning logic could be automated based on on-chain events, such as a user's stake falling below a threshold or a governance vote to revoke status. Always ensure these external calls are secure to prevent manipulation.

Finally, thoroughly test your minting and burning logic. Write unit tests that verify: transfers between users revert, minting works only for the minter role, burning works only for the burner role, and the token owner cannot initiate either action. This ensures your reputation token behaves as a truly non-transferable, programmatically managed record of on-chain standing.

revocation-mechanism
SOULBOUND TOKEN IMPLEMENTATION

Step 3: Adding a Revocation Mechanism

Implement a secure method for authorized entities to revoke a user's non-transferable token, a critical feature for managing reputation and compliance.

A revocation mechanism is essential for any practical Soulbound Token (SBT) system. Unlike standard ERC-20 or ERC-721 tokens, a non-transferable reputation token must have a controlled exit path. This allows the issuing entity—such as a DAO, protocol, or certification body—to remove a token if the holder's status changes, for example, due to malicious behavior, expired credentials, or a community vote. Without this, a compromised or undeserved reputation is permanently locked on-chain.

The standard approach is to implement an access control pattern, where only a designated address (like a DAO multisig or governance contract) can call a revoke function. A basic Solidity implementation adds a state variable for the revoker and a function that burns the holder's token. It's crucial that this function includes permission checks and emits a clear event for off-chain tracking.

solidity
function revoke(address holder) external onlyRevoker {
    require(balanceOf(holder) > 0, "No token to revoke");
    _burn(holder);
    emit Revoked(holder, block.timestamp);
}

For more complex governance, you can integrate with a voting contract like OpenZeppelin Governor, making revocation contingent on a successful proposal. Alternatively, you can design time-locked revocations or implement a suspension state that temporarily disables token utility without burning it. Always ensure the revocation logic is transparent and documented for users to build trust in the system's fairness.

Consider the user experience and data implications. A simple _burn removes the token entirely, which may be desirable for severe violations. However, for audit trails, you might want to preserve a record. One pattern is to mint a separate "revoked" version of the SBT to a null address or a history contract, maintaining a public, immutable record of the action and its reason.

Thoroughly test the revocation function. Write unit tests that verify: the onlyRevoker modifier works, non-revokers cannot call the function, the holder's balance goes to zero, and the correct event is emitted. This function grants significant power, so its security and the integrity of the revoker's private keys are paramount.

public-query-interface
DATA ACCESS

Step 4: Building a Public Query Interface

Make your reputation data programmatically accessible to other dApps and services by building a public query interface.

A public query interface allows external applications to read and verify the reputation data stored in your non-transferable token (SBT) contracts. This is essential for enabling composability—other protocols can trustlessly integrate your reputation scores into their own logic. The interface typically exposes a set of view functions on your smart contract that return data without requiring a transaction or gas fee. Common queries include fetching a user's current score, checking if they hold a specific badge, or retrieving their complete on-chain reputation history.

Design your interface around the most likely use cases. For a lending protocol, you might expose a function like getCreditScore(address user). For a governance system, you could provide getVotingWeight(address user) or hasQualifiedBadge(address user, uint badgeId). Use the ERC-165 standard to publish your contract's interface ID, making it easily discoverable by other developers. This standard allows contracts to declare which interfaces they implement, so integrators can programmatically verify compatibility.

Here is a basic Solidity example of a query interface for a reputation SBT:

solidity
interface IReputationQuery {
    function getReputationScore(address holder) external view returns (uint256);
    function getBadgeIds(address holder) external view returns (uint256[] memory);
    function hasBadge(address holder, uint256 badgeId) external view returns (bool);
    function getScoreHistory(address holder, uint256 limit) external view returns (uint256[] memory);
}

Your main reputation contract would then implement this interface. The functions should be gas-optimized for reading, often by storing data in efficiently accessible mappings or arrays.

Consider indexing and serving historical data, which can be resource-intensive to calculate on-chain. For complex queries—like a user's score trend over the last 90 days—you may need to integrate with an off-chain indexer like The Graph. This indexer listens to your contract's events, processes the data into a structured database (subgraph), and provides a GraphQL endpoint for fast, complex queries. You can then create a helper function in your contract that references this external endpoint, or build a separate API service that aggregates on-chain and indexed data.

Finally, document your query interface thoroughly. Provide the contract ABI, example calls using ethers.js or viem, and sample responses. List the EVM chain IDs where your contract is deployed. Good documentation is critical for adoption. By building a robust, documented query layer, you transform your reputation system from a closed silo into an open, programmable primitive that can be woven into the broader DeFi and governance ecosystem.

testing-deployment
LAUNCHING A NON-TRANSFERABLE REPUTATION TOKEN

Testing and Deployment

This guide covers the final steps to test and deploy a secure, on-chain Soulbound Token (SBT) contract.

Before deploying your Soulbound Token contract to a mainnet, thorough testing is critical. Start by writing unit tests in a framework like Hardhat or Foundry to verify core functionalities: token minting, the enforcement of non-transferability, and role-based access control for your MINTER_ROLE. Test edge cases such as attempting to transfer a token via transfer or transferFrom, which should always revert. Also, test the safeMint function to ensure only authorized addresses can issue tokens and that minting to the zero address or an address that already holds a token fails appropriately.

After unit tests pass, proceed to integration testing on a local or testnet fork. Use tools like Hardhat Network to simulate mainnet conditions. Deploy your contract and your chosen upgradeability proxy (e.g., TransparentUpgradeableProxy from OpenZeppelin) to a testnet like Sepolia or Goerli. Execute a full minting flow and interact with the contract using a front-end dApp or scripts to confirm the user experience matches expectations. This stage is also the time to verify any integration with off-chain systems, such as an API that grants minting permissions.

For the final deployment, choose a deployment script that handles proxy initialization. A typical script will first deploy the implementation contract, then deploy the proxy contract that points to it, and finally call an initialization function on the proxy to set up the MINTER_ROLE and any other parameters. Always verify your contract source code on a block explorer like Etherscan after deployment. For upgradeable contracts, you must verify both the implementation and the proxy contract. This transparency is essential for user trust in your reputation system.

Post-deployment, establish clear management procedures. Securely store the private keys for the admin and minter roles, preferably using a multisig wallet or a dedicated secure service. Document the contract address, ABI, and any relevant interfaces for your dApp or community. Monitor the contract for events and consider setting up alerts for critical functions. Remember, while the logic can be upgraded, the non-transferable nature of the tokens is enforced by the initial implementation's overrides of transfer functions, a core property that should never be altered in an upgrade.

SOULBOUND TOKENS

Frequently Asked Questions (FAQ)

Answers to common technical questions and troubleshooting issues for developers implementing non-transferable reputation tokens using standards like ERC-721S and ERC-5192.

ERC-721S and ERC-5192 are the two primary standards for non-transferable tokens, but they take different approaches.

ERC-721S (Soulbound) is a standalone standard that extends ERC-721. Its core feature is a hard-coded, immutable locked state. Once a token is minted to an address, the transferFrom and safeTransferFrom functions are permanently disabled. This is enforced at the contract level.

ERC-5192 (Minimal Soulbound) is a lighter, "minimal" interface standard. It doesn't modify transfer logic itself. Instead, it requires a locked() function that returns a bool. If locked() returns true, front-ends and marketplaces should treat the token as non-transferable. The actual transfer restriction logic must be implemented separately (e.g., overriding transfer functions to revert).

Key Takeaway: Use ERC-721S for a self-contained, strictly enforced solution. Use ERC-5192 for greater flexibility or to add soulbound properties to an existing contract, relying on external UI/UX to respect the locked flag.

conclusion
IMPLEMENTATION GUIDE

Conclusion and Next Steps

You have now explored the core concepts and technical architecture for building a non-transferable reputation token. This section summarizes the key takeaways and outlines practical steps for launching your own system.

Building a Soulbound Token (SBT) system is fundamentally about creating a verifiable, on-chain record of identity and achievements that cannot be bought or sold. The core technical components you must implement are: a smart contract enforcing non-transferability (often by overriding the _beforeTokenTransfer hook in an ERC-721 or ERC-1155 contract), a secure and permissioned minting mechanism, and a clear data schema for the token's metadata. Remember, the trust and utility of the system depend entirely on the integrity of the issuer—the entity with minting rights. Your contract's access controls are its most critical security feature.

Before deploying to mainnet, rigorously test your implementation. Use a development framework like Hardhat or Foundry to write tests that verify: tokens cannot be transferred between wallets, minting is restricted to authorized issuers, and metadata is correctly stored and retrieved. Consider deploying first to a testnet like Sepolia or Goerli to simulate real-world conditions. For metadata, decide between on-chain storage (expensive but immutable) and decentralized solutions like IPFS or Arweave (cost-effective, requires persistent pinning). Tools like NFT.Storage or Pinata can simplify this process.

Your next step is to integrate the SBTs into a broader application. This typically involves building a frontend dApp that allows users to connect their wallet (using WalletConnect or MetaMask), view their held SBTs, and potentially claim new ones via a verified process. The backend or issuer service should listen for on-chain events or provide a secure API for triggering mints. Explore existing standards like EIP-4973 (Account-bound Tokens) for community-vetted interfaces, but be prepared for the ecosystem's evolving nature. Always prioritize user experience around wallet security and gasless transactions where possible.

Finally, consider the long-term governance and utility of your reputation system. How will you handle revocation or updates to a token's status if the underlying achievement is invalidated? Designing a transparent and fair process for this is essential. Look to established projects in the space, such as Gitcoin Passport for decentralized identity aggregation or Orange Protocol for attestation frameworks, to understand different design patterns. The true power of SBTs is unlocked when they become interoperable credentials, usable across multiple applications in a decentralized ecosystem.

How to Launch a Non-Transferable Reputation Token (Soulbound) | ChainScore Guides