Back to Lessons
Lesson 19

Access-Controlled & Compliant Tokens

+25EFFORT
5 sections
security()
01
text

Production and regulated tokens often need controls beyond mint and burn: pausing transfers during an incident, blocklisting sanctioned or hacked addresses, capping the maximum supply, and assigning permissions to multiple parties. OpenZeppelin provides Pausable, AccessControl (named roles), and ERC20Capped as composable building blocks.

02
code
solidity
1contract Token is ERC20, AccessControl, Pausable, ERC20Capped {
2 bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
3 bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
4
5 function mint(address to, uint256 amt) external onlyRole(MINTER_ROLE) {
6 _mint(to, amt); // ERC20Capped reverts if this exceeds cap()
7 }
8 function pause() external onlyRole(PAUSER_ROLE) { _pause(); }
9
10 // Transfers are blocked while paused (enforced in the _update hook)
11 function _update(address from, address to, uint256 v)
12 internal override whenNotPaused { super._update(from, to, v); }
13}
03
text

Each control is also a centralization power. A pause can halt an exploit — or freeze legitimate users. Blocklists (used by USDC and USDT) let the issuer freeze stolen funds, but also mean the issuer can freeze YOU. ERC20Capped enforces a hard maximum supply. Role-based access lets you separate duties — a minter that cannot pause, a pauser that cannot mint — instead of one all-powerful owner.

04
note
Key Insight

Audit the powers: read who holds DEFAULT_ADMIN_ROLE (it can grant every other role) and the mint/pause roles. Those keys should live behind a timelock or multisig, not a single externally-owned account. 'Who can freeze or inflate this token, and how are those keys secured?' is one of the most important questions about any token.

05
code
solidity
1// Capped supply enforcement (simplified):
2function _update(address from, address to, uint256 value) internal override {
3 super._update(from, to, value);
4 if (from == address(0)) { // a mint
5 require(totalSupply() <= cap(), "cap exceeded");
6 }
7}
Complete

Connect your wallet to mark this lesson as complete

Earn 25 EFFORT tokens