@@ -21,138 +21,128 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
2121/// @notice A basic ERC20 compatible token contract with burn and minting roles.
2222/// @dev The total supply can be limited during deployment.
2323contract BurnMintERC20 is IBurnMintERC20 , IGetCCIPAdmin , IERC165 , ERC20Burnable , AccessControl {
24- error MaxSupplyExceeded (uint256 supplyAfterMint );
25- error InvalidRecipient (address recipient );
26-
27- event CCIPAdminTransferred (address indexed previousAdmin , address indexed newAdmin );
28-
29- /// @dev The number of decimals for the token
30- uint8 internal immutable i_decimals;
31-
32- /// @dev The maximum supply of the token, 0 if unlimited
33- uint256 internal immutable i_maxSupply;
34-
35- /// @dev the CCIPAdmin can be used to register with the CCIP token admin registry, but has no other special powers,
36- /// and can only be transferred by the owner.
37- address internal s_ccipAdmin;
38-
39- bytes32 public constant MINTER_ROLE = keccak256 ("MINTER_ROLE " );
40- bytes32 public constant BURNER_ROLE = keccak256 ("BURNER_ROLE " );
41-
42- /// @dev the underscores in parameter names are used to suppress compiler warnings about shadowing ERC20 functions
43- constructor (
44- string memory name ,
45- string memory symbol ,
46- uint8 decimals_ ,
47- uint256 maxSupply_ ,
48- uint256 preMint
49- ) ERC20 (name, symbol) {
50- i_decimals = decimals_;
51- i_maxSupply = maxSupply_;
52-
53- s_ccipAdmin = msg .sender ;
54-
55- // Mint the initial supply to the new Owner, saving gas by not calling if the mint amount is zero
56- if (preMint != 0 ) _mint (msg .sender , preMint);
57-
58- // Set up the owner as the initial minter and burner
59- _grantRole (DEFAULT_ADMIN_ROLE, msg .sender );
60- }
61-
62- /// @inheritdoc IERC165
63- function supportsInterface (
64- bytes4 interfaceId
65- ) public pure virtual override (AccessControl, IERC165 ) returns (bool ) {
66- return interfaceId == type (IERC20 ).interfaceId || interfaceId == type (IBurnMintERC20).interfaceId
67- || interfaceId == type (IERC165 ).interfaceId || interfaceId == type (IAccessControl).interfaceId
68- || interfaceId == type (IGetCCIPAdmin).interfaceId;
69- }
70-
71- // ================================================================
72- // │ ERC20 │
73- // ================================================================
74-
75- /// @dev Returns the number of decimals used in its user representation.
76- function decimals () public view virtual override returns (uint8 ) {
77- return i_decimals;
78- }
79-
80- /// @dev Returns the max supply of the token, 0 if unlimited.
81- function maxSupply () public view virtual returns (uint256 ) {
82- return i_maxSupply;
83- }
84-
85- // ================================================================
86- // │ Burning & minting │
87- // ================================================================
88-
89- /// @inheritdoc ERC20Burnable
90- /// @dev Uses OZ ERC20 _burn to disallow burning from address(0).
91- /// @dev Decreases the total supply.
92- function burn (
93- uint256 amount
94- ) public virtual override (IBurnMintERC20, ERC20Burnable ) onlyRole (BURNER_ROLE) {
95- super .burn (amount);
96- }
97-
98- /// @inheritdoc IBurnMintERC20
99- /// @dev Alias for BurnFrom for compatibility with the older naming convention.
100- /// @dev Uses burnFrom for all validation & logic.
101- function burn (address account , uint256 amount ) public virtual override {
102- burnFrom (account, amount);
103- }
104-
105- /// @inheritdoc ERC20Burnable
106- /// @dev Uses OZ ERC20 _burn to disallow burning from address(0).
107- /// @dev Decreases the total supply.
108- function burnFrom (
109- address account ,
110- uint256 amount
111- ) public virtual override (IBurnMintERC20, ERC20Burnable ) onlyRole (BURNER_ROLE) {
112- super .burnFrom (account, amount);
113- }
114-
115- /// @inheritdoc IBurnMintERC20
116- /// @dev Uses OZ ERC20 _mint to disallow minting to address(0).
117- /// @dev Disallows minting to address(this)
118- /// @dev Increases the total supply.
119- function mint (address account , uint256 amount ) external virtual override onlyRole (MINTER_ROLE) {
120- if (account == address (this )) revert InvalidRecipient (account);
121- if (i_maxSupply != 0 && totalSupply () + amount > i_maxSupply) revert MaxSupplyExceeded (totalSupply () + amount);
122-
123- _mint (account, amount);
124- }
125-
126- // ================================================================
127- // │ Roles │
128- // ================================================================
129-
130- /// @notice grants both mint and burn roles to `burnAndMinter`.
131- /// @dev calls public functions so this function does not require
132- /// access controls. This is handled in the inner functions.
133- function grantMintAndBurnRoles (
134- address burnAndMinter
135- ) external virtual {
136- grantRole (MINTER_ROLE, burnAndMinter);
137- grantRole (BURNER_ROLE, burnAndMinter);
138- }
139-
140- /// @notice Returns the current CCIPAdmin
141- function getCCIPAdmin () external view virtual returns (address ) {
142- return s_ccipAdmin;
143- }
144-
145- /// @notice Transfers the CCIPAdmin role to a new address
146- /// @dev only the owner can call this function, NOT the current ccipAdmin, and 1-step ownership transfer is used.
147- /// @param newAdmin The address to transfer the CCIPAdmin role to. Setting to address(0) is a valid way to revoke
148- /// the role
149- function setCCIPAdmin (
150- address newAdmin
151- ) external virtual onlyRole (DEFAULT_ADMIN_ROLE) {
152- address currentAdmin = s_ccipAdmin;
153-
154- s_ccipAdmin = newAdmin;
155-
156- emit CCIPAdminTransferred (currentAdmin, newAdmin);
157- }
24+ error MaxSupplyExceeded (uint256 supplyAfterMint );
25+ error InvalidRecipient (address recipient );
26+
27+ event CCIPAdminTransferred (address indexed previousAdmin , address indexed newAdmin );
28+
29+ /// @dev The number of decimals for the token
30+ uint8 internal immutable i_decimals;
31+
32+ /// @dev The maximum supply of the token, 0 if unlimited
33+ uint256 internal immutable i_maxSupply;
34+
35+ /// @dev the CCIPAdmin can be used to register with the CCIP token admin registry, but has no other special powers,
36+ /// and can only be transferred by the owner.
37+ address internal s_ccipAdmin;
38+
39+ bytes32 public constant MINTER_ROLE = keccak256 ("MINTER_ROLE " );
40+ bytes32 public constant BURNER_ROLE = keccak256 ("BURNER_ROLE " );
41+
42+ /// @dev the underscores in parameter names are used to suppress compiler warnings about shadowing ERC20 functions
43+ constructor (string memory name , string memory symbol , uint8 decimals_ , uint256 maxSupply_ , uint256 preMint )
44+ ERC20 (name, symbol)
45+ {
46+ i_decimals = decimals_;
47+ i_maxSupply = maxSupply_;
48+
49+ s_ccipAdmin = msg .sender ;
50+
51+ // Mint the initial supply to the new Owner, saving gas by not calling if the mint amount is zero
52+ if (preMint != 0 ) _mint (msg .sender , preMint);
53+
54+ // Set up the owner as the initial minter and burner
55+ _grantRole (DEFAULT_ADMIN_ROLE, msg .sender );
56+ }
57+
58+ /// @inheritdoc IERC165
59+ function supportsInterface (bytes4 interfaceId ) public pure virtual override (AccessControl, IERC165 ) returns (bool ) {
60+ return interfaceId == type (IERC20 ).interfaceId || interfaceId == type (IBurnMintERC20).interfaceId
61+ || interfaceId == type (IERC165 ).interfaceId || interfaceId == type (IAccessControl).interfaceId
62+ || interfaceId == type (IGetCCIPAdmin).interfaceId;
63+ }
64+
65+ // ================================================================
66+ // │ ERC20 │
67+ // ================================================================
68+
69+ /// @dev Returns the number of decimals used in its user representation.
70+ function decimals () public view virtual override returns (uint8 ) {
71+ return i_decimals;
72+ }
73+
74+ /// @dev Returns the max supply of the token, 0 if unlimited.
75+ function maxSupply () public view virtual returns (uint256 ) {
76+ return i_maxSupply;
77+ }
78+
79+ // ================================================================
80+ // │ Burning & minting │
81+ // ================================================================
82+
83+ /// @inheritdoc ERC20Burnable
84+ /// @dev Uses OZ ERC20 _burn to disallow burning from address(0).
85+ /// @dev Decreases the total supply.
86+ function burn (uint256 amount ) public virtual override (IBurnMintERC20, ERC20Burnable ) onlyRole (BURNER_ROLE) {
87+ super .burn (amount);
88+ }
89+
90+ /// @inheritdoc IBurnMintERC20
91+ /// @dev Alias for BurnFrom for compatibility with the older naming convention.
92+ /// @dev Uses burnFrom for all validation & logic.
93+ function burn (address account , uint256 amount ) public virtual override {
94+ burnFrom (account, amount);
95+ }
96+
97+ /// @inheritdoc ERC20Burnable
98+ /// @dev Uses OZ ERC20 _burn to disallow burning from address(0).
99+ /// @dev Decreases the total supply.
100+ function burnFrom (address account , uint256 amount )
101+ public
102+ virtual
103+ override (IBurnMintERC20, ERC20Burnable )
104+ onlyRole (BURNER_ROLE)
105+ {
106+ super .burnFrom (account, amount);
107+ }
108+
109+ /// @inheritdoc IBurnMintERC20
110+ /// @dev Uses OZ ERC20 _mint to disallow minting to address(0).
111+ /// @dev Disallows minting to address(this)
112+ /// @dev Increases the total supply.
113+ function mint (address account , uint256 amount ) external virtual override onlyRole (MINTER_ROLE) {
114+ if (account == address (this )) revert InvalidRecipient (account);
115+ if (i_maxSupply != 0 && totalSupply () + amount > i_maxSupply) revert MaxSupplyExceeded (totalSupply () + amount);
116+
117+ _mint (account, amount);
118+ }
119+
120+ // ================================================================
121+ // │ Roles │
122+ // ================================================================
123+
124+ /// @notice grants both mint and burn roles to `burnAndMinter`.
125+ /// @dev calls public functions so this function does not require
126+ /// access controls. This is handled in the inner functions.
127+ function grantMintAndBurnRoles (address burnAndMinter ) external virtual {
128+ grantRole (MINTER_ROLE, burnAndMinter);
129+ grantRole (BURNER_ROLE, burnAndMinter);
130+ }
131+
132+ /// @notice Returns the current CCIPAdmin
133+ function getCCIPAdmin () external view virtual returns (address ) {
134+ return s_ccipAdmin;
135+ }
136+
137+ /// @notice Transfers the CCIPAdmin role to a new address
138+ /// @dev only the owner can call this function, NOT the current ccipAdmin, and 1-step ownership transfer is used.
139+ /// @param newAdmin The address to transfer the CCIPAdmin role to. Setting to address(0) is a valid way to revoke
140+ /// the role
141+ function setCCIPAdmin (address newAdmin ) external virtual onlyRole (DEFAULT_ADMIN_ROLE) {
142+ address currentAdmin = s_ccipAdmin;
143+
144+ s_ccipAdmin = newAdmin;
145+
146+ emit CCIPAdminTransferred (currentAdmin, newAdmin);
147+ }
158148}
0 commit comments