Abstract

This proposal introduces a protocol that establishes a unified interface for managing both ERC-20 fungible tokens and ERC-721 non-fungible tokens (NFTs) on the Ethereum blockchain. By defining a common set of functions applicable to both token types, developers can seamlessly interact with ERC-20 and ERC-721 tokens using a single interface. This simplifies integration efforts and enhances interoperability within decentralized applications (DApps).

Motivation

The proposal aims to address the demand for assets combining the liquidity of ERC-20 tokens and the uniqueness of ERC-721 tokens. Current standards present a fragmentation, requiring users to choose between these features. This proposal fills that gap by providing a unified token interface, enabling smooth transitions between ERC-20 and ERC-721 characteristics to accommodate diverse blockchain applications.

Specification

  • Introduces a token contract that combines features from both ERC-20 and ERC-721 standards.
  • Supports state transitions between ERC-20 and ERC-721 modes, facilitating seamless conversion and utilization of both liquidity and non-fungibility.
  • Defines essential functions and events to support token interactions, conversions, and queries.
  • Implements low gas consumption ERC-20 mode to maintain efficiency comparable to typical ERC-20 token transfers.

Compliant contracts MUST implement the following Solidity interface:


pragma solidity ^0.8.0;
/**
 * @title ERC-7629 Unify Token Interface
 * @dev This interface defines the ERC-7629 Unify Token, which unifies ERC-721 and ERC-20 assets.
 */
interface IERC7629  is IERC165 {
    // ERC-20 Transfer event
    event ERC20Transfer(
        address indexed from,
        address indexed to,
        uint256 amount
    );

    // ERC-721 Transfer event
    event ERC721Transfer(
        address indexed from,
        address indexed to,
        uint256 indexed tokenId
    );

    // ERC-721 Transfer event
    event Transfer(
        address indexed from,
        address indexed to,
        uint256 indexed tokenId
    );

    // Approval event for ERC-20 and ERC-721
    event Approval(
        address indexed owner,
        address indexed approved,
        uint256 indexed tokenId
    );

    // Approval event for ERC-20 and ERC-721
    event Approval(
        address indexed owner,
        address indexed approved,
        uint256 indexed tokenId
    );

    // Approval event for ERC-20
    event ERC20Approval(
        address indexed owner,
        address indexed approved,
        uint256 indexed tokenId
    );

    // ApprovalForAll event for ERC-721
    event ApprovalForAll(
        address indexed owner,
        address indexed operator,
        bool approved
    );

    // ERC-20 to ERC-721 Conversion event
    event ERC20ToERC721(address indexed to, uint256 amount, uint256 tokenId);

    // ERC-721 to ERC-20 Conversion event
    event ERC20ToERC721(address indexed to, uint256 amount, uint256[] tokenIds);

    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the number of decimals used in the token.
     */
    function decimals() external view returns (uint8);

    /**
     * @dev Returns the total supply of the ERC-20 tokens.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the balance of an address for ERC-20 tokens.
     * @param owner The address to query the balance of.
     */
    function balanceOf(address owner) external view returns (uint256);

    /**
     * @dev Returns the total supply of ERC-20 tokens.
     */
    function erc20TotalSupply() external view returns (uint256);

    /**
     * @dev Returns the balance of an address for ERC-20 tokens.
     * @param owner The address to query the balance of.
     */
    function erc20BalanceOf(address owner) external view returns (uint256);

    /**
     * @dev Returns the total supply of ERC-721 tokens.
     */
    function erc721TotalSupply() external view returns (uint256);

    /**
     * @dev Returns the balance of an address for ERC-721 tokens.
     * @param owner The address to query the balance of.
     */
    function erc721BalanceOf(address owner) external view returns (uint256);

    /**
     * @notice Get the approved address for a single NFT
     * @dev Throws if `tokenId` is not a valid NFT.
     * @param tokenId The NFT to find the approved address for
     * @return The approved address for this NFT, or the zero address if there is none
     */
    function getApproved(uint256 tokenId) external view returns (address);

    /**
     * @dev Checks if an operator is approved for all tokens of a given owner.
     * @param owner The address of the token owner.
     * @param operator The address of the operator to check.
     */
    function isApprovedForAll(
        address owner,
        address operator
    ) external view returns (bool);

    /**
     * @dev Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner.
     * @param owner The address of the token owner.
     * @param spender The address of the spender.
     */
    function allowance(
        address owner,
        address spender
    ) external view returns (uint256);

    /**
     * @dev Returns the array of ERC-721 token IDs owned by a specific address.
     * @param owner The address to query the tokens of.
     */
    function owned(address owner) external view returns (uint256[] memory);

    /**
     * @dev Returns the address that owns a specific ERC-721 token.
     * @param tokenId The token ID.
     */
    function ownerOf(uint256 tokenId) external view returns (address erc721Owner);

    /**
     * @dev Returns the URI for a specific ERC-721 token.
     * @param tokenId The token ID.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);

    /**
     * @dev Approve or disapprove the operator to spend or transfer all of the sender's tokens.
     * @param spender The address of the spender.
     * @param amountOrId The amount of ERC-20 tokens or ID of ERC-721 tokens.
     */
    function approve(
        address spender,
        uint256 amountOrId
    ) external returns (bool);

    /**
     * @dev Set or unset the approval of an operator for all tokens.
     * @param operator The address of the operator.
     * @param approved The approval status.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev Transfer ERC-20 tokens or ERC-721 token from one address to another.
     * @param from The address to transfer ERC-20 tokens or ERC-721 token from.
     * @param to The address to transfer ERC-20 tokens or ERC-721 token to.
     * @param amountOrId The amount of ERC-20 tokens or ID of ERC-721 tokens to transfer.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amountOrId
    ) external returns (bool);
    
    /**
     * @notice Transfers the ownership of an NFT from one address to another address
     * @dev Throws unless `msg.sender` is the current owner, an authorized
     *  operator, or the approved address for this NFT. Throws if `_rom` is
     *  not the current owner. Throws if `_to` is the zero address. Throws if
     *  `tokenId` is not a valid NFT. When transfer is complete, this function
     *  checks if `to` is a smart contract (code size > 0). If so, it calls
     *  `onERC721Received` on `to` and throws if the return value is not
     *  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
     * @param from The current owner of the NFT
     * @param to The new owner
     * @param tokenId The NFT to transfer
     * @param data Additional data with no specified format, sent in call to `to`
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external payable;

    /**
     * @notice Transfers the ownership of an NFT from one address to another address
     * @dev This works identically to the other function with an extra data parameter,
     *  except this function just sets data to "".
     * @param from The current owner of the NFT
     * @param to The new owner
     * @param tokenId The NFT to transfer
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) external payable;

    /**
     * @dev Transfer ERC-20 tokens to an address.
     * @param to The address to transfer ERC-20 tokens to.
     * @param amount The amount of ERC-20 tokens to transfer.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Retrieves the unit value associated with the token.
     * @return The unit value.
     */
    function getUnit() external view returns (uint256);

    /**
     * @dev Converts ERC-721 token to ERC-20 tokens.
     * @param tokenId The unique identifier of the ERC-721 token.
     */
    function erc721ToERC20(uint256 tokenId) external;

    /**
     * @dev Converts ERC-20 tokens to an ERC-721 token.
     * @param amount The amount of ERC-20 tokens to convert.
     */
    function erc20ToERC721(uint256 amount) external;
}


Rationale

Common Interface for Different Token Types:

  • Introduces a unified interface to address the fragmentation caused by separate ERC-20 and ERC-721 standards.
  • Standardizes functions like transferFrom, mint, and burn, enabling developers to interact with both token types without implementing distinct logic.

Transfer Functionality:

  • Includes transferFrom function for seamless movement of tokens between addresses, as it’s a core component of both ERC-20 and ERC-721 standards.

Minting and Burning:

  • Incorporates mint and burn functions for creating and destroying tokens, essential for managing token supply and lifecycle.

Balance and Ownership Queries:

  • Provides functions like balanceOf and ownerOf for retrieving token balances and ownership information, crucial for both ERC-20 and ERC-721 tokens.

Compatibility and Extensibility:

  • Ensures compatibility with existing ERC-20 and ERC-721 implementations, minimizing disruption during transition.
  • Allows extension with additional functions and events for future enhancements.

Security Considerations:

  • Implements mechanisms to prevent common issues like reentrancy attacks and overflows, ensuring the security and robustness of the unified interface.

Backwards Compatibility

The proposed this proposal introduces a challenge in terms of backward compatibility due to the distinct balance query mechanisms utilized by ERC-20 and ERC-721 standards. ERC-20 employs balanceOf to check an account’s token balance, while ERC-721 uses balanceOf to inquire about the quantity of tokens owned by an account. To reconcile these differences, the ERC must consider providing either two separate functions catering to each standard or adopting a more generalized approach.

Compatibility Points

The primary compatibility point lies in the discrepancy between ERC-20’s balanceOf and ERC-721’s balanceOf functionalities. Developers accustomed to the specific balance query methods in each standard may face challenges when transitioning to this proposal.

Proposed Solutions

Dual Balance Query Functions:

Introduce two distinct functions, erc20BalanceOf and erc721TotalSupply, to align with the conventions of ERC-20 and ERC-721, respectively. Developers can choose the function based on the token type they are working with.

Security Considerations

  • Due to the dual nature of this proposal, potential differences in protocol interpretation may arise, necessitating careful consideration during development.
  • Comprehensive security audits are recommended, especially during mode transitions by users, to ensure the safety of user assets.

Copyright and related rights waived via CC0.