This EIP adds refund functionality for initial token offerings to EIP-20, EIP-721, and EIP-1155. Funds are held in escrow until a predetermined time before they are claimable. Until that predetermined time passes, users can receive a refund for tokens they have purchased.
Motivation
The NFT and token spaces lack accountability. For the health of the ecosystem as a whole, better mechanisms to prevent rugpulls from happening are needed. Offering refunds provides greater protection for buyers and increases legitimacy for creators.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.
EIP-20 Refund Extension
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.17;import"ERC20.sol";import"ERC165.sol";/// @notice Refundable EIP-20 tokens
/// @dev The EIP-165 identifier of this interface is `0xf0ca2917`
interfaceERC20RefundisERC20,ERC165{/// @notice Emitted when a token is refunded
/// @dev Emitted by `refund`
/// @param _sender The person that requested a refund
/// @param _amount The amount of token (in terms of the smallest divisible unit) that was refunded
eventRefund(addressindexed_sender,uint256indexed_amount);/// @notice As long as the refund is active, refunds the user
/// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors
/// @param amount The `amount` to refund
functionrefund(uint256amount)external;/// @notice Gets the refund price
/// @return _wei The amount of ether (in wei) that would be refunded for a single token unit (10**decimals smallest divisible units)
functionrefundOf()externalviewreturns(uint256_wei);/// @notice Gets the first block for which the refund is not active
/// @return block The first block where the token cannot be refunded
functionrefundDeadlineOf()externalviewreturns(uint256block);}
EIP-721 Refund Extension
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.17;import"ERC721.sol";import"ERC165.sol";/// @notice Refundable EIP-721 tokens
/// @dev The EIP-165 identifier of this interface is `0xe97f3c83`
interfaceERC721RefundisERC721/* , ERC165 */{/// @notice Emitted when a token is refunded
/// @dev Emitted by `refund`
/// @param _sender The person that requested a refund
/// @param _tokenId The `tokenId` that was refunded
eventRefund(addressindexed_sender,uint256indexed_tokenId);/// @notice As long as the refund is active for the given `tokenId`, refunds the user
/// @dev Make sure to check that the user has the token, and be aware of potential re-entrancy vectors
/// @param tokenId The `tokenId` to refund
functionrefund(uint256tokenId)external;/// @notice Gets the refund price of the specific `tokenId`
/// @param tokenId The `tokenId` to query
/// @return _wei The amount of ether (in wei) that would be refunded
functionrefundOf(uint256tokenId)externalviewreturns(uint256_wei);/// @notice Gets the first block for which the refund is not active for a given `tokenId`
/// @param tokenId The `tokenId` to query
/// @return block The first block where token cannot be refunded
functionrefundDeadlineOf(uint256tokenId)externalviewreturns(uint256block);}
EIP-1155 Refund Extension
// SPDX-License-Identifier: CC0-1.0
pragmasolidity^0.8.17;import"ERC1155.sol";import"ERC165.sol";/// @notice Refundable EIP-1155 tokens
/// @dev The EIP-165 identifier of this interface is `0x94029f5c`
interfaceERC1155RefundisERC1155/* , ERC165 */{/// @notice Emitted when a token is refunded
/// @dev Emitted by `refund`
/// @param _sender The person that requested a refund
/// @param _tokenId The `tokenId` that was refunded
/// @param _amount The amount of `tokenId` that was refunded
eventRefund(addressindexed_sender,uint256indexed_tokenId,uint256_amount);/// @notice As long as the refund is active for the given `tokenId`, refunds the user
/// @dev Make sure to check that the user has enough tokens, and be aware of potential re-entrancy vectors
/// @param tokenId The `tokenId` to refund
/// @param amount The amount of `tokenId` to refund
functionrefund(uint256tokenId,uint256amount)external;/// @notice Gets the refund price of the specific `tokenId`
/// @param tokenId The `tokenId` to query
/// @return _wei The amount of ether (in wei) that would be refunded for a single token
functionrefundOf(uint256tokenId)externalviewreturns(uint256_wei);/// @notice Gets the first block for which the refund is not active for a given `tokenId`
/// @param tokenId The `tokenId` to query
/// @return block The first block where the token cannot be refunded
functionrefundDeadlineOf(uint256tokenId)externalviewreturns(uint256block);}
Rationale
refundDeadlineOf uses blocks instead of timestamps, as timestamps are less reliable than block numbers.
The function names of refund, refundOf, and refundDeadlineOf were chosen to fit the naming style of EIP-20, EIP-721, and EIP-1155.
EIP-165 is required as introspection by DApps would be made significantly harder if it weren’t.
Custom EIP-20 tokens are not supported, as it needlessly increases complexity.
Backwards Compatibility
No backward compatibility issues were found.
Security Considerations
There is a potential re-entrancy risk with the refund function. Make sure to perform the ether transfer after the tokens are destroyed (i.e. obey the checks, effects, interactions pattern).