Basic Implementation (RBAC and Pause)
This guide details the mandatory technical requirements for contracts that do not use the Diamond pattern. Any contract outside ISBE's native schema must ensure its interoperability with the network's administration and monitoring systems.
1. Access Controls (RBAC)
All contracts must implement a Role-Based Access Control (RBAC) system. The central requirement is the existence of a Super Admin role with absolute privileges and external irrevocability.
Example: RBAC with OpenZeppelin AccessControl
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title ISBEAccessControl
* @notice RBAC implementation aligned with ISBE guidelines.
*
* Guaranteed critical invariants:
*
* 1. SUPER_ADMIN_ROLE is self-managed: only the holder or
* another Super Admin can designate a new one.
*
* 2. External irrevocability: revokeRole is overridden to
* prevent any account from revoking another's SUPER_ADMIN_ROLE.
* Only self-renunciation (renounceRole) is allowed.
*
* 3. Permission inheritance: the Super Admin receives all
* lower roles in the constructor, satisfying the requirement
* for absolute privileges over contract operations.
*/
contract ISBEAccessControl is AccessControl {
bytes32 public constant SUPER_ADMIN_ROLE = keccak256("SUPER_ADMIN_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
constructor(address superAdmin) {
// SUPER_ADMIN_ROLE manages itself
_grantRole(SUPER_ADMIN_ROLE, superAdmin);
_setRoleAdmin(SUPER_ADMIN_ROLE, SUPER_ADMIN_ROLE);
// Lower roles: their admin is always SUPER_ADMIN_ROLE
_setRoleAdmin(PAUSER_ROLE, SUPER_ADMIN_ROLE);
_setRoleAdmin(MINTER_ROLE, SUPER_ADMIN_ROLE);
_setRoleAdmin(BURNER_ROLE, SUPER_ADMIN_ROLE);
_setRoleAdmin(UPGRADER_ROLE, SUPER_ADMIN_ROLE);
// Role inheritance: the Super Admin holds all roles
_grantRole(PAUSER_ROLE, superAdmin);
_grantRole(MINTER_ROLE, superAdmin);
_grantRole(BURNER_ROLE, superAdmin);
_grantRole(UPGRADER_ROLE, superAdmin);
}
/**
* @dev Override revokeRole to protect SUPER_ADMIN_ROLE.
* External revocation of the Super Admin is blocked.
* Only the holder can renounce it via renounceRole.
*/
function revokeRole(bytes32 role, address account)
public
override
onlyRole(getRoleAdmin(role))
{
require(
role != SUPER_ADMIN_ROLE,
"ISBE: SUPER_ADMIN_ROLE is externally irrevocable; use renounceRole"
);
super.revokeRole(role, account);
}
}
2. Circuit Breakers (Pause Mechanisms)
It is imperative that contract execution can be halted by the administration in case of emergencies.
Custom Contracts (whenOperational Modifier)
If the contract does not allow direct inheritance from Pausable, a custom modifier must be included that supports global and granular pause by function selector (msg.sig).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title ISBECustomContract
* @notice Contract with a custom pause modifier according to ISBE
* guidelines for custom business logic contracts.
* Supports global pause and granular pause by function selector.
*/
contract ISBECustomContract is AccessControl {
bytes32 public constant SUPER_ADMIN_ROLE = keccak256("SUPER_ADMIN_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bool private _globalPause;
// Selective pause per function: selector bytes4 => paused
mapping(bytes4 => bool) private _functionPaused;
event GlobalPaused(address indexed by);
event GlobalUnpaused(address indexed by);
event FunctionPaused(bytes4 indexed selector, address indexed by);
event FunctionUnpaused(bytes4 indexed selector, address indexed by);
constructor(address superAdmin) {
_grantRole(SUPER_ADMIN_ROLE, superAdmin);
_setRoleAdmin(SUPER_ADMIN_ROLE, SUPER_ADMIN_ROLE);
_setRoleAdmin(PAUSER_ROLE, SUPER_ADMIN_ROLE);
_grantRole(PAUSER_ROLE, superAdmin);
}
// ---------------------------------------------------------------
// MANDATORY modifier: must be applied to ALL external and
// public functions that modify state (state-changing).
// msg.sig contains the 4-byte selector of the active function.
// ---------------------------------------------------------------
modifier whenOperational() {
require(!_globalPause, "ISBE: contract globally paused");
require(!_functionPaused[msg.sig], "ISBE: function paused");
_;
}
// ---------------------------------------------------------------
// Global pause control
// ---------------------------------------------------------------
function pauseGlobal() external onlyRole(PAUSER_ROLE) {
_globalPause = true;
emit GlobalPaused(msg.sender);
}
function unpauseGlobal() external onlyRole(PAUSER_ROLE) {
_globalPause = false;
emit GlobalUnpaused(msg.sender);
}
// ---------------------------------------------------------------
// Granular pause control per function
// ---------------------------------------------------------------
function pauseFunction(bytes4 selector) external onlyRole(PAUSER_ROLE) {
_functionPaused[selector] = true;
emit FunctionPaused(selector, msg.sender);
}
function unpauseFunction(bytes4 selector) external onlyRole(PAUSER_ROLE) {
_functionPaused[selector] = false;
emit FunctionUnpaused(selector, msg.sender);
}
// ---------------------------------------------------------------
// Example of protected business function
// Replicates the "whenOperational" pattern on all state-changing
// functions of the contract.
// ---------------------------------------------------------------
function executeOperation(address target, uint256 amount)
external
whenOperational
{
// business logic here
}
}
3. Data Protection and Privacy
Given the immutable nature of blockchain technology, a comprehensive audit of data handling is required.
Golden Rules for Data on ISBE
- PII on-chain Prohibition: Storing Personally Identifiable Information (PII) or sensitive data in plain text is strictly forbidden.
- Cryptographic Anonymization: All private data must be processed using anonymization techniques (hash functions) prior to persistence.
- Right to be Forgotten: It is recommended to store only hashes on-chain that point to external data ("salt") whose deletion makes the hash comprehension irreversible.
4. Type Auditing
Special technical justification is required for the use of string types or dynamic bytes arrays in on-chain storage, as they often constitute false obfuscation of data that should be handled off-chain.
Token Standards (Native Pausable)
For ERC-20, ERC-721, or ERC-1155, the Pausable extension (e.g., ERC20Pausable) should be inherited to atomically block transfers.