Soulbound NFT as separate standard

I’ve been thinking about current Soulbound token implementations and their reliance on ERC-721 as a base. Most existing solutions (like ERC-5192) essentially bolt locking mechanisms onto ERC-721, reverting transfers with custom errors. While this works, it feels architecturally wrong.

The core issue: Soulbound tokens aren’t non-fungible - they’re non-transferable. ERC-721 is built around the concept of transferability. The entire interface revolves around transferFrom, safeTransferFrom, approve, setApprovalForAll - functions that fundamentally contradict what Soulbound means.

When we inherit from ERC-721 and override these functions to revert, we’re carrying dead weight:

  • Storage slots for approvals and operators that will never be used
  • Larger deployment bytecode
  • Higher gas costs on mint/burn due to unnecessary state management
  • Increased Ethereum state growth from unused mappings

Here’s a minimal implementation that covers everything Soulbound needs:

interface SoulboundNFT {
    event Mint(address to, uint256 id);
    event Burn(address from, uint256 id);

    function mint() external returns(uint256, bool);
    function burn(uint256) external;
    function tokenURI(uint256 _tokenId) external view returns (string memory);
}

interface IERC721Metadata {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

contract SoulboundToken is SoulboundNFT, IERC721Metadata {
    uint256 private tokenId;
    string private _name;
    string private _symbol;
    string private _baseURI;
    
    mapping(uint256 => address) public ownerOf;
    
    error ErrNotAnOwner();

    constructor(string memory name_, string memory symbol_, string memory baseURI_) {
        _name = name_;
        _symbol = symbol_;
        _baseURI = baseURI_;
    }

    function name() external view override returns (string memory) {
        return _name;
    }

    function symbol() external view override returns (string memory) {
        return _symbol;
    }

    function tokenURI(uint256 _tokenId) external view override returns (string memory) {
        if (ownerOf[_tokenId] == address(0)) revert ErrNotAnOwner();
        return _baseURI;
    }

    function mint() external virtual returns (uint256 id, bool) {
        tokenId += 1;
        id = tokenId;
        ownerOf[id] = msg.sender;
        emit Mint(msg.sender, id);
        return (id, true);
    }

    function burn(uint256 id) external virtual {
        if (ownerOf[id] != msg.sender) revert ErrNotAnOwner();
        ownerOf[id] = address(0);
        emit Burn(msg.sender, id);
    }
}

This drastically reduces codesize, which opens up space for storing additional data directly in the contract - like on-chain SVG metadata.

The semantic argument matters too. When you see ERC-721, you expect transferability. Soulbound tokens represent credentials, achievements, identity - things that by definition shouldn’t move between addresses. Using a transferable token standard and disabling transfers creates conceptual confusion.

Current ERC-721-based SBT implementations aren’t truly “bound to soul” - they’re transferable tokens with transfer restrictions. That’s a workaround, not a solution.

A dedicated standard would:

  • Remove unused storage (approvals, operators)
  • Reduce deployment bytecode significantly
  • Lower gas costs for mint/revoke operations
  • Decrease state growth compared to ERC-721-based implementations
  • Provide clear semantics: mint once, own forever, or burn

The counterargument is ecosystem compatibility - wallets and marketplaces already understand ERC-721. But Soulbound tokens don’t need marketplace support. They’re not meant to be traded. Wallet support just needs ownerOf and metadata URI, which any minimal standard can provide.

Curious what others think - is the convenience of ERC-721 inheritance worth the architectural and efficiency tradeoffs?

1 Like

Adoption (by wallets & explorers) of a new standard would likely take years, even if there was a compelling application. (based on my experience with ERC721).

That doesn’t mean you shouldn’t do it, but it would be a long road.

1 Like

I understand that this may take a long time, but I am more interested in the implementation of the idea and the community’s response. If the community responds, then it is worth it; if the community does not care, then there is probably no point in pushing this idea

1 Like