Ankita.eth
GithubContact
  • About Ankita
  • experience
    • TECHNOLOGIES
    • Frontend
      • Javascript
      • React
      • NextJS
      • HTML & CSS
      • UI Libraries & Frameworks
        • Tailwind CSS
        • Comprehensive Guide to UI Libraries and Frameworks
    • Backend
      • Node.js
      • Express.js
    • Database
      • Mongodb, Mongoose
      • PostgresSQl
      • MySQL
    • Packege Mangers
      • NPM-Node Packege Manager
      • Yarn
      • Yarn 2 (Berry)
      • PNPM
      • BUN
      • Commands cheatsheet
    • API Providers
      • Alchemy
      • Telegram Bot
      • CoinMarket
      • Thirdweb
      • Infura
      • Moralis
    • DevOps/Infrastructure
      • Docker
      • Kubernetes
      • CI/CD
      • Docker Swam
    • Protocols
      • ERCs & EIPs
        • ERC-20
        • ERC-721
        • ERC-1155
        • ERC-4337
        • ERC-6551
        • ERC-777
        • ERC-3643
        • EIP-7702
        • ERC-7715
        • ERC-7739
        • EIP-6780
        • EIP-5792
        • ERC-4626
        • EIP-1559
        • ERC-404
        • ERC-3643
        • ERC-223
    • Web3 Toolkits
      • Foundry
      • Hardhat
      • RemixIDE
    • Messaging/Caching
      • Kafka
      • Redis
      • Sendgrid
    • Blockchain
      • Solana
      • Ethereum
      • Polygon & Zero knowldge Proof
      • Bitcoin
      • Solidity
    • Deployment Platforms
      • AWS
      • Vercel
      • Heroku, Render
      • Domain setup
  • SDKs
    • Google Cloud SDK
    • AWS SDK
    • Firebase SDK
  • EOF EVM Object Format
  • Articles
    • Medium Articles
    • 🌐 My Work
  • 📞 Get in Touch
Powered by GitBook
On this page
  • 📜 EIP-7702: Soulbound Token Standard
  • 🏗️ Architecture
  • 🧠 Core Concepts
  • 🚶 User Flow
  • 💻 Implementation Details
  • 🚀 Example Implementation

Was this helpful?

  1. experience
  2. Protocols
  3. ERCs & EIPs

EIP-7702

PreviousERC-3643NextERC-7715

Last updated 8 months ago

Was this helpful?

📜 EIP-7702: Soulbound Token Standard

EIP-7702 introduces a standard for Soulbound Tokens (SBTs) in the Ethereum ecosystem. This page will provide a comprehensive overview of its architecture, core concepts, user flow, and implementation details.

🏗️ Architecture

The EIP-7702 standard defines a set of interfaces and functionalities for Soulbound Tokens. Here's a high-level overview of the architecture:

🧠 Core Concepts

1. Non-Transferability 🔒

SBTs are designed to be non-transferable, meaning once minted to an address, they cannot be moved to another address.

2. Revocability 🔄

The issuer of an SBT has the ability to revoke (burn) the token if necessary.

3. Metadata 📊

Each SBT can carry metadata, providing additional information about the token and its properties.

4. Enumeration 🔢

The standard includes enumeration functions to easily query and iterate over tokens.

🚶 User Flow

Here's a typical user flow for interacting with Soulbound Tokens:

💻 Implementation Details

Core Interface (IERC7702)

interface IERC7702 {
    event Minted(address indexed to, uint256 indexed tokenId);
    event Burned(address indexed from, uint256 indexed tokenId);

    function mint(address to) external returns (uint256 tokenId);
    function burn(uint256 tokenId) external;
    function ownerOf(uint256 tokenId) external view returns (address owner);
}

Metadata Interface (IERC7702Metadata)

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

Enumerable Interface (IERC7702Enumerable)

interface IERC7702Enumerable is IERC7702 {
    function totalSupply() external view returns (uint256);
    function tokenByIndex(uint256 index) external view returns (uint256);
    function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
}

🚀 Example Implementation

Here's a basic implementation of the ERC7702 standard:

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract ERC7702 is IERC7702, IERC7702Metadata, IERC7702Enumerable {
    using Counters for Counters.Counter;
    using Strings for uint256;

    string private _name;
    string private _symbol;
    Counters.Counter private _tokenIds;
    mapping(uint256 => address) private _owners;
    mapping(address => uint256[]) private _ownedTokens;

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

    function mint(address to) external override returns (uint256) {
        _tokenIds.increment();
        uint256 newTokenId = _tokenIds.current();
        _mint(to, newTokenId);
        return newTokenId;
    }

    function burn(uint256 tokenId) external override {
        require(msg.sender == ownerOf(tokenId), "ERC7702: caller is not the owner");
        _burn(tokenId);
    }

    function ownerOf(uint256 tokenId) public view override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC7702: invalid token ID");
        return owner;
    }

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

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

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        require(_exists(tokenId), "ERC7702: URI query for nonexistent token");
        return string(abi.encodePacked("<https://example.com/token/>", tokenId.toString()));
    }

    function totalSupply() public view override returns (uint256) {
        return _tokenIds.current();
    }

    function tokenByIndex(uint256 index) public view override returns (uint256) {
        require(index < totalSupply(), "ERC7702: global index out of bounds");
        return index + 1;
    }

    function tokenOfOwnerByIndex(address owner, uint256 index) public view override returns (uint256) {
        require(index < balanceOf(owner), "ERC7702: owner index out of bounds");
        return _ownedTokens[owner][index];
    }

    function balanceOf(address owner) public view returns (uint256) {
        return _ownedTokens[owner].length;
    }

    function _mint(address to, uint256 tokenId) internal {
        require(to != address(0), "ERC7702: mint to the zero address");
        require(!_exists(tokenId), "ERC7702: token already minted");

        _owners[tokenId] = to;
        _ownedTokens[to].push(tokenId);

        emit Minted(to, tokenId);
    }

    function _burn(uint256 tokenId) internal {
        address owner = ownerOf(tokenId);

        delete _owners[tokenId];
        _removeTokenFromOwnerEnumeration(owner, tokenId);

        emit Burned(owner, tokenId);
    }

    function _exists(uint256 tokenId) internal view returns (bool) {
        return _owners[tokenId] != address(0);
    }

    function _removeTokenFromOwnerEnumeration(address owner, uint256 tokenId) private {
        uint256 lastTokenIndex = _ownedTokens[owner].length - 1;
        uint256 tokenIndex;

        for (uint256 i = 0; i <= lastTokenIndex; i++) {
            if (_ownedTokens[owner][i] == tokenId) {
                tokenIndex = i;
                break;
            }
        }

        if (tokenIndex != lastTokenIndex) {
            _ownedTokens[owner][tokenIndex] = _ownedTokens[owner][lastTokenIndex];
        }
        _ownedTokens[owner].pop();
    }
}

This implementation provides a solid foundation for creating Soulbound Tokens following the EIP-7702 standard. Developers can extend and customize this base implementation to suit their specific use cases and requirements.