Là một smart contract thuộc thư viện OpenZeppelin cho phép ta thực hiện kiểm soát quyền truy cập dựa trên vai trò (tương tự như RBAC của database).

Mỗi role được thể hiện bằng một ID có kiểu bytes32, thường được quy ước là dùng hàm keccak256 trênmột chuỗi nào đó:

bytes32 public constant MINT_AND_BURN_ROLE = keccak256("MINT_AND_BURN_ROLE");

Các role được quản lý thông qua một mapping như sau:

struct RoleData {
	mapping(address account => bool) hasRole;
	bytes32 adminRole;
}
 
mapping(bytes32 role => RoleData) private _roles;

Để cấp quyền, có thể dùng hàm grantRole hoặc _grantRole của AccessControl:

/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
	_grantRole(role, account);
}
 
/**
 * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
 *
 * Internal function without access restriction.
 *
 * May emit a {RoleGranted} event.
 */
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
	if (!hasRole(role, account)) {
		_roles[role].hasRole[account] = true;
		emit RoleGranted(role, account, _msgSender());
		return true;
	} else {
		return false;
	}
}

Với admin role mặc định có ID là:

bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

Smart contract AccessControl cung cấp hàm hasRole để kiểm tra một address có nắm giữ role nào đó và function modifier onlyRole để giới hạn quyền thực thi hàm lại cho một role nào đó.

/**
 * @dev Returns `true` if `account` has been granted `role`.
 */
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
	return _roles[role].hasRole[account];
}
 
/* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
 * is missing `role`.
 */
function _checkRole(bytes32 role, address account) internal view virtual {
	if (!hasRole(role, account)) {
		revert AccessControlUnauthorizedAccount(account, role);
	}
}
/**
 * @dev Modifier that checks that an account has a specific role. Reverts
 * with an {AccessControlUnauthorizedAccount} error including the required role.
 */
modifier onlyRole(bytes32 role) {
	_checkRole(role);
	_;
}

Để sử dụng trong smart contract, ta cần kế thừa smart contract AccessControl:

import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
 
contract RebaseToken is ERC20, Ownable, AccessControl {
 
}

Sau đó gọi hàm _grantRole như trên để gán quyền trong một hàm nào đó:

function grantMintAndBurnRole(address _account) external onlyOwner {
    _grantRole(MINT_AND_BURN_ROLE, _account);
}

Important

Việc không thực hiện gán quyền trong constructor là để đảm bảo không xảy ra circular dependencies xảy ra khi contract này đang khởi tạo nhưng cần cấp quyền cho contract khác chưa được khởi tạo.

Cuối cùng, sử dụng onlyRole để đảm bảo rằng chỉ những address có role được specified mới được thực thi hàm:

/**
 * @notice Mint principal to a user (e.g., on deposit). Also mints accrued interest and locks their rate.
 */
function mint(
	address _to,
	uint256 _amount
) external onlyRole(MINT_AND_BURN_ROLE) {
	// invoke the internal function for calculating the accrued interest (in rebase token) and mint it for the user
	_mintAccruedInterest(_to);
 
	// take the snap shot of the interest rate and store it
	s_userInterestRate[_to] = s_interestRate;
 
	// do the minting by invoking the internal _mint of ERC20
	_mint(_to, _amount);
}