TIP-1006: Burn At for TIP-20 Tokens
Abstract
This specification introduces a burnAt function to TIP-20 tokens, allowing holders of a new BURN_AT_ROLE to burn tokens from any address without transfer policy restrictions. This complements the existing burnBlocked function which is limited to burning from addresses blocked by the transfer policy.
Motivation
The existing TIP-20 burn mechanisms have the following limitations:
burn()- Only burns from the caller's own balance, requiresISSUER_ROLEburnBlocked()- Can burn from other addresses, but only if the target address is blocked by the transfer policy
There are legitimate use cases where token administrators may want a privileged caller to have the ability to burn tokens from any address regardless of their policy status, such as allowing a bridge contract to burn tokens that are being bridged out without requiring approval (as in the crosschainBurn function proposed in ERC 7802).
The burnAt function provides this capability with appropriate access controls via a dedicated role.
Specification
New Role
A new role constant is added to TIP-20:
bytes32 public constant BURN_AT_ROLE = keccak256("BURN_AT_ROLE");This role is administered by the DEFAULT_ADMIN_ROLE (same as other TIP-20 roles).
New Event
/// @notice Emitted when tokens are burned from any account.
/// @param from The address from which tokens were burned.
/// @param amount The amount of tokens burned.
event BurnAt(address indexed from, uint256 amount);New Function
/// @notice Burns tokens from any account.
/// @dev Requires BURN_AT_ROLE. Cannot burn from protected precompile addresses.
/// @param from The address to burn tokens from.
/// @param amount The amount of tokens to burn.
function burnAt(address from, uint256 amount) external;Behavior
- Access Control: Reverts with
Unauthorizedif caller does not haveBURN_AT_ROLE - Protected Addresses: Reverts with
ProtectedAddressiffromis:TIP_FEE_MANAGER_ADDRESS(0xfeEC000000000000000000000000000000000000)STABLECOIN_DEX_ADDRESS(0xDEc0000000000000000000000000000000000000)
- Balance Check: Reverts with
InsufficientBalanceiffromhas insufficient balance - No Policy Check: Unlike
burnBlocked, this function does NOT check transfer policy authorization - State Changes:
- Decrements
balanceOf[from]byamount - Decrements
_totalSupplybyamount - Updates reward accounting if
fromis opted into rewards
- Decrements
- Events: Emits
Transfer(from, address(0), amount)andBurnAt(from, amount)
Interface Addition
The ITIP20 interface is extended with:
/// @notice Returns the role identifier for burning tokens from any account.
/// @return The burn-at role identifier.
function BURN_AT_ROLE() external view returns (bytes32);
/// @notice Burns tokens from any account.
/// @param from The address to burn tokens from.
/// @param amount The amount of tokens to burn.
function burnAt(address from, uint256 amount) external;Invariants
- Role Required:
burnAtmust always revert if caller lacksBURN_AT_ROLE - Protected Addresses:
burnAtmust never succeed whenfromis a protected precompile address - Supply Conservation: After
burnAt(from, amount):totalSupplydecreases by exactlyamountbalanceOf[from]decreases by exactlyamount
- Balance Constraint:
burnAtmust revert ifamount > balanceOf[from] - Reward Accounting: If
fromis opted into rewards,optedInSupplymust decrease byamount - Policy Independence:
burnAtmust succeed regardless of transfer policy status offrom
Test Cases
The test suite must verify:
- Successful burn with
BURN_AT_ROLE - Revert without
BURN_AT_ROLE(Unauthorized) - Revert when burning from
TIP_FEE_MANAGER_ADDRESS(ProtectedAddress) - Revert when burning from
STABLECOIN_DEX_ADDRESS(ProtectedAddress) - Successful burn from policy-blocked address (differs from
burnBlocked) - Revert on insufficient balance
- Correct event emissions (
TransferandBurnAt) - Correct reward accounting updates