The experimental semi-fungible token standard, ERC-404, combines elements from ERC-20 and ERC-721 tokens. Despite rising popularity, it has yet to secure an official Ethereum Improvement Proposal (EIP) designation. However, its unique attributes, such as enabling fractional ownership of NFTs and enhancing liquidity, coupled with the potential for automated NFT minting and burning processes, suggest a strong likelihood of attaining EIP status soon, benefiting Web3 developers with streamlined transactions and enhanced functionality.
ERC-404 allows users to create semi-fungible tokens supporting fungible (ERC-20) and non-fungible (ERC-721) token features. This unlocks the potential for concepts like Fractional Non-Fungible Token (NFT) ownership and an expanded utility and liquidity of digital assets. This complex functionality is achieved by effectively isolating the ERC-20 and ERC-721 logics via a process called “pathing.”
Pathing, as described by the developers, is “a lossy encoding scheme in which the token amount data and IDs occupy shared space under the assumption that negligible token transfers occupying id space do not or do not need to occur.”
Two anonymous developers, “crtl” and “Acme” from Pandora Labs, recently released the standard’s second beta version with some improvements.
The ERC-404 token standard introduces an advanced framework for managing digital assets through dynamic buying, selling, and transferring mechanisms. This standard automates the minting of NFTs when a wallet’s token balance exceeds a whole number, effectively transforming the excess into a unique, tradable asset. Conversely, when the balance falls below this threshold, the corresponding NFTs are burned, permanently disassociating them from both the account and the fungible token.
This process seamlessly combines the fluidity of traditional cryptocurrency transactions with the unique, collectible nature of NFTs, offering a streamlined yet flexible way for users to engage with digital assets. By doing so, ERC-404 tokens elevate interactivity and utility within the blockchain ecosystem, merging fungibility and uniqueness into a cohesive system.
This innovative approach enhances digital asset interactivity and utility, bridging the gap between fungible and non-fungible tokens. ERC-404 provides a versatile framework for engaging with the blockchain.
Balance Logic
ERC-404 v1.0 standard must be able to track the balance of ERC-20 tokens and the ownership of ERC-721 tokens effectively. The ERC-20 balance is tracked separately using mappings:
uint8 public immutable decimals; mapping(address => uint256) public balanceOf;
balanceOf[address]
” keeps track of the ERC-20 token balance for each address. Ownership Logic
ERC-721 ownership is tracked with the following logic:
mapping(uint256 => address) internal _ownerOf;
_ownerOf[tokenId]
” keeps track of the owner of each ERC-721 token ID.
mapping(uint256 => address) internal _ownerOf;
_owned[address]
” stores an array of token IDs owned by each address.
mapping(uint256 => uint256) internal _ownedIndex;
_ownedIndex[tokenId]
” stores the token ID’s index in the _owned array
for quick access and efficient removal.ERC-20 Transfer Logic
In addition to this efficient logic, ERC-404 adopts an innovative approach to handle ERC-20 and ERC-721 token transfers. The key components of the ERC-20 transfer logic are as follows:
function _transfer(
address from,
address to,
uint256 amount
) internal returns (bool) {
uint256 unit = _getUnit();
uint256 balanceBeforeSender = balanceOf[from];
uint256 balanceBeforeReceiver = balanceOf[to];
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
// Skip burn for certain addresses to save gas
if (!whitelist[from]) {
uint256 tokens_to_burn = (balanceBeforeSender / unit) - (balanceOf[from] / unit);
for (uint256 i = 0; i < tokens_to_burn; i++) {
_burn(from);
}
}
// Skip minting for certain addresses to save gas
if (!whitelist[to]) {
uint256 tokens_to_mint = (balanceOf[to] / unit) - (balanceBeforeReceiver / unit);
for (uint256 i = 0; i < tokens_to_mint; i++) {
_mint(to);
}
}
emit ERC20Transfer(from, to, amount);
return true;
}
_getUnit()
. balanceBeforeSender
) and receiver (balanceBeforeReceiver
) before the transfer. _burn
accordingly. _mint
accordingly. ERC20Transfer
event to log the transfer. Return true to indicate success.ERC-721 Transfer Logic
The ERC-721 token uses the same _transfer()
function as core logic. However, since it has to handle ownership of the NFTs, the logic contains changes.
function transferFrom(
address from,
address to,
uint256 amountOrId
) public virtual {
if (amountOrId <= minted) {
if (from != _ownerOf[amountOrId]) {
revert InvalidSender();
}
if (to == address(0)) {
revert InvalidRecipient();
}
if (
msg.sender != from &&
!isApprovedForAll[from][msg.sender] &&
msg.sender != getApproved[amountOrId]
) {
revert Unauthorized();
}
balanceOf[from] -= _getUnit();
unchecked {
balanceOf[to] += _getUnit();
}
_ownerOf[amountOrId] = to;
delete getApproved[amountOrId];
// Update _owned for sender
uint256 updatedId = _owned[from][_owned[from].length - 1];
_owned[from][_ownedIndex[amountOrId]] = updatedId;
_owned[from].pop();
_ownedIndex[updatedId] = _ownedIndex[amountOrId];
// Add token to receiver's owned list
_owned[to].push(amountOrId);
_ownedIndex[amountOrId] = _owned[to].length - 1;
emit Transfer(from, to, amountOrId);
emit ERC20Transfer(from, to, _getUnit());
} else {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max)
allowance[from][msg.sender] = allowed - amountOrId;
_transfer(from, to, amountOrId);
}
}
amountOrId
” is less than or equal to the number of minted tokens, which indicates it’s an ERC-721 token ID._ownerOf
”._owned
” array and “_ownedIndex
” mapping for the sender to reflect the removal of the token ID._owned
” array and update the “_ownedIndex
” mapping.amountOrId
is not an ERC-721 token ID, treat it as an ERC-20 transfer amount._transfer()
” function to handle the ERC-20 transfer logic as described earlier.Insights About v1
The second iteration of ERC-404 contains some key updates to the code logic.
DoubleEndedQueue.Uint256Deque
” for managing ERC-721 token IDs in a FIFO queue.
DoubleEndedQueue.Uint256Deque private _storedERC721Ids;
function erc20BalanceOf(address owner_) public view virtual returns (uint256);
function erc721BalanceOf(address owner_) public view virtual returns (uint256);
function erc20TotalSupply() public view virtual returns (uint256);
function erc721TotalSupply() public view virtual returns (uint256);
function _transferERC20WithERC721(address from_, address to_, uint256 value_) internal virtual returns (bool);
function _transferERC20(address from_, address to_, uint256 value_) internal virtual;
function _transferERC721(address from_, address to_, uint256 id_) internal virtual;
_mintERC20()
and _retrieveOrMintERC721()
functions to make the minting process easier and more dedicated.
function _mintERC20(address to_, uint256 value_) internal virtual;
function _retrieveOrMintERC721(address to_) internal virtual;
function permit(address owner_, address spender_, uint256 value_, uint256 deadline_, uint8 v_, bytes32 r_, bytes32 s_) public virtual;
function DOMAIN_SEPARATOR() public view virtual returns (bytes32);
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool);
As the logic explained above suggests, the ERC-404 is mostly compatible with the ERC-20 and ERC-721 standards. However, this does not mean that there are no differences between them.
Fungibility | Token Representation | Metadata | Functions and Interfaces | |
ERC-404 | Semi fungible | Identical & Unique Token Support | No Metadata | Standard functions for fungible tokens. |
ERC-20 | Fungible | Identical Tokens | Metadata for each Token | Standard functions for ownership and transfer of unique tokens. |
ERC-721 | Non-Fungible | Unique Tokens | Metadata for each Token | Would need to combine functions from both ERC-20 and ERC-721, potentially leading to a more complex interface. |
ERC-404 is an unofficial and unaudited standard. This lack of formal validation and the fact that many developers lack specific knowledge about it create risks when using it in projects. This section highlights key points to be cautious of for both versions of ERC-404.
Missing Input Validation: The constructor and the transferFrom() function do not validate their inputs. It is crucial to ensure the provided values adhere to the protocol’s specific operational boundaries as laid out in the project specifications and documentation. If the constructors or functions lack appropriate validation checks, there’s a risk of setting state variables with values that could cause unexpected and potentially detrimental behavior within the contract’s operations, violating the intended logic of the protocol.
Gas Optimizations: Math operations, loops with high ranges and calls, and array sizes should be carefully implemented (with proper limits and caps) and tested before launching the project. If these are neglected, the gas cost can increase exponentially, and the project can experience gas-related problems such as out-of-gas exceptions or exceeding block gas limits.
Reentrancy Risk: When implementing and using transfer and allowance mechanisms, developers should follow the Checks-Effects-Interactions pattern. If this pattern fails or a valid mutex lock is not implemented, the created project can experience problems like Reentrancy attacks.
Signature Malleability: ECDSA signature verification allows for slight modifications to the signature that do not change the signature’s validity. This can result in various issues, including access control, contract execution, or data integrity problems. Attackers can exploit this vulnerability to manipulate signed data or perform unauthorized transactions, leading to severe security risks. Implementing a cryptographic signature system in Ethereum contracts often assumes that the signature is unique, but signatures can be altered without possessing the private key and still be valid. The EVM specification defines several so-called ‘precompiled’ contracts, one of them being ecrecover, which executes the elliptic curve public key recovery. A malicious user can slightly modify the three values v, r, and s to create other valid signatures. A system that performs signature verification on the contract level might be susceptible to attacks if the signature is part of the signed message hash. Valid signatures could be created by a malicious user to replay previously signed messages.
Follow @hackenclub on 𝕏 (Twitter)
ERC-404 marks a significant advancement in blockchain with its semi-fungible token standard, merging the best of ERC-20 and ERC-721 tokens. It introduces innovative features like fractional NFT ownership and improved liquidity, although it is still experimental and awaits official EIP endorsement. The standard enables automated NFT minting and burning, simplifies transactions, and reduces costs, which is especially advantageous for Web3 developers who focus on digital asset utility and interactivity.
However, its unofficial status and lack of formal audits require developers to exercise caution regarding potential security vulnerabilities such as input validation and reentrancy risks. Addressing these issues is vital for secure and effective project use.
Overall, ERC-404 symbolizes significant progress in blending fungible and non-fungible tokens, with the potential to become a key tool for digital asset innovation as it evolves and possibly receives formal recognition.
Be the first to receive our latest company updates, Web3 security insights, and exclusive content curated for the blockchain enthusiasts.
Table of contents
Tell us about your project
6 min read
Discover
10 min read
Discover
4 min read
Discover