Abstract
ERC-7629, known as the Unified Token Protocol, introduces a comprehensive protocol unifying the characteristics of ERC-721 and ERC-20 tokens within the Ethereum ecosystem. This standard seamlessly integrates the liquidity features of ERC-20 with the non-fungible nature of ERC-721, enabling frictionless conversion between these asset types. ERC-7629 offers a multifunctional solution, providing developers and users with the flexibility to leverage both liquidity and non-fungibility in a unified token framework.
Motivation
The motivation behind ERC-7629 stems from the inherent need within the blockchain community for assets that possess both the liquidity of ERC-20 tokens and the non-fungibility of ERC-721 tokens. Current standards present a dichotomy, necessitating users to choose between these features. ERC-7629 addresses this limitation by providing a unified token standard, empowering users to seamlessly transition between ERC-20 and ERC-721 characteristics, catering to diverse blockchain applications.
Specification
- ERC-7629 standardizes a token contract that encompasses features from both ERC-20 and ERC-721.
- The token contract supports state transitions between ERC-20 and ERC-721 modes, ensuring smooth conversion and utilization of both liquidity and non-fungibility.
- Defines essential functions and events to support token interactions, conversions, and queries.
- Implements a low gas consumption ERC-20 mode to maintain gas 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
The rationale for ERC-7629 lies in its ability to harmonize liquidity and non-fungibility, filling a crucial gap in the existing token standards. By unifying ERC-20 and ERC-721 features, ERC-7629 enhances user flexibility, allowing for efficient conversion between these asset types. The protocol is designed to provide a standardized and versatile solution for developers, promoting a more streamlined development process. With smooth transitions and low gas consumption, ERC-7629 aims to be a robust standard that not only simplifies token interactions but also fosters broader adoption within the Ethereum ecosystem.
Backwards Compatibility
The proposed ERC-7629 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 ERC-7629.
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.
Test Cases
Reference Implementation
- ERC-7629 standard undergoes thorough testing to ensure consistent and accurate behavior in both ERC-20 and ERC-721 modes.
- Detailed documentation and sample code are provided to assist developers in understanding and implementing the ERC-7629 standard.
Security Considerations
- Due to the dual nature of ERC-7629, 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.