betterERC
Docs/Reference

Extensions and their data

What is installed, how each identifier is derived, and how to decode a module's live configuration.

All docs

extensions() returns the installed set as an array of identifiers, and hasExtension(id) answers for one. Both revert until the assembly has sealed its registry, so a token that forgot to finalise has no discovery surface at all rather than a quietly empty one.

IDNameDeclares
0x1880c1f5erc20.extension.onchainMetadatanothing
0xe420f71eerc20.extension.transferFeeFEE_ON_TRANSFER
0x72fd4318erc20.extension.transferRestrictionPAUSABLE, BLOCKLIST
0x2c0ebf42erc20.extension.nonTransferableNON_TRANSFERABLE
0xf71cd3feerc20.extension.transferHookTRANSFER_HOOK

Why these are not interface IDs

An identifier here is the first four bytes of a hash over a name, not an interface ID derived from an interface's selectors. An interface gains functions over time, and an identifier that moved when it did would break every integrator who cached it.

The names also carry no draft ERC number, because a draft number is a placeholder that changes on the way to Final and would take every deployed token's identifiers with it.

Reading a module's configuration

extensionData(id) returns the module's live configuration, ABI-encoded. It reverts with ERC20ExtensionNotEnabled when the module is not installed, which is what lets you distinguish installed but unconfigured — empty bytes — from not installed at all.

ExtensionDecodes as
onchainMetadata(string tokenURI, uint256 keyCount)
transferFee(uint16 basisPoints, uint256 maximumFee, address vault)
transferRestriction(bool paused)
nonTransferableempty
transferHook(address hook, uint32 gasLimit)

Staying current without polling

Every configuration change in every module emits one shared event carrying the extension's identifier and its full configuration after the change. A single subscription keeps an indexer's view of the whole token accurate; the per-module events are still emitted alongside, for consumers who only care about one.

IERC20Extensions.sol
event ExtensionConfigured(bytes4 indexed extensionId, bytes data);