> For the complete documentation index, see [llms.txt](https://www.ankitavirani.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.ankitavirani.com/experience/protocols/ercs-and-eips/eip-7702.md).

# EIP-7702

## 📜 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:

<figure><img src="/files/MwtnckSWkgpdkjwp7v0Y" alt=""><figcaption></figcaption></figure>

### 🧠 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:

<figure><img src="/files/0rmRZmKJOccwKCoZAQK7" alt=""><figcaption></figcaption></figure>

### 💻 Implementation Details

#### Core Interface (IERC7702)

```solidity
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)

```solidity
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)

```solidity
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:

```solidity
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.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.ankitavirani.com/experience/protocols/ercs-and-eips/eip-7702.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
