Events and callbacks
Extensions can choose to listen to specific token events on-chain (such as transfers or approvals) which trigger specific checks or actions.
Info
The TokenExtension
base contract provides helper methods to listen to common token events.
The only thing extensions cannot do inside an event callback is perform an action that would cause the same event to be triggered again. This is to prevent reentry attacks and potential recursion problems.
_listenForTokenTransfers(function (TransferData memory) external returns (bool) callback)
: listens for token transfers and invokes the provided callback function. When the callback is invoked, the transfer has already occurred._listenForTokenApprovals(function (TransferData memory) external returns (bool) callback)
: listens for token approvals and invokes the provided callback function. When the callback is invoked, the approval has already occurred._listenForTokenBeforeTransfers(function (TransferData memory) external returns (bool) callback)
: listens for token transfers and invokes the provided callback function. The callback is invoked right before the transfer occurs.
If an extension wishes to listen to an event, it should subscribe to the event inside its initialize
function. However, an extension can choose to subscribe whenever they like.
import {TokenExtension, TransferData} from "@consensys-software/UniversalToken-extendable/contracts/extensions/TokenExtension.sol";
contract PauseExtension is TokenExtension {
bytes32 constant internal ALLOWLIST_ROLE = keccak256("allowblock.roles.allowlisted");
constructor() {
// ...
}
function initialize() external override {
_listenForTokenTransfers(this.onTransferExecuted);
}
function onTransferExecuted(TransferData memory data) external onlyToken returns (bool) {
// hasRole is provided by the TokenExtension base contract
bool fromAllowed = data.from == address(0) || hasRole(data.from, ALLOWLIST_ROLE);
bool toAllowed = data.to == address(0) || hasRole(data.to, ALLOWLIST_ROLE);
require(fromAllowed, "from address is not allowlisted");
require(toAllowed, "to address is not allowlisted");
return fromAllowed && toAllowed;
}
}
Important
- Event callback functions must be marked as either
external
orpublic
and MUST include theonlyToken
modifier. - The
onlyToken
modifier ensures that only the token can execute the callback function.