Implementación Básica (RBAC y Pausa)
Esta guía detalla los requisitos técnicos obligatorios para contratos que no utilizan el patrón Diamond. Todo contrato ajeno al esquema nativo de ISBE debe asegurar su interoperabilidad con los sistemas de administración y monitoreo de la red.
1. Controles de Acceso (RBAC)
Todos los contratos deben implementar un sistema de Control de Acceso Basado en Roles (RBAC). El requisito central es la existencia de un rol Super Admin con privilegios absolutos e irrevocabilidad externa.
Ejemplo: RBAC con OpenZeppelin AccessControl
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title ISBEAccessControl
* @notice Implementacion de RBAC alineada con las directrices ISBE.
*
* Invariantes criticas garantizadas:
*
* 1. SUPER_ADMIN_ROLE es auto-administrado: solo el titular o
* otro Super Admin puede designar uno nuevo.
*
* 2. Irrevocabilidad externa: revokeRole esta sobreescrito para
* impedir que cualquier cuenta revoque el SUPER_ADMIN_ROLE
* de otro. Solo se permite la auto-renuncia (renounceRole).
*
* 3. Herencia de permisos: el Super Admin recibe todos los roles
* inferiores en el constructor, satisfaciendo el requisito de
* privilegios absolutos sobre la operativa del contrato.
*/
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 se administra a si mismo
_grantRole(SUPER_ADMIN_ROLE, superAdmin);
_setRoleAdmin(SUPER_ADMIN_ROLE, SUPER_ADMIN_ROLE);
// Roles inferiores: su admin es siempre 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);
// Herencia de permisos: el Super Admin posee todos los roles
_grantRole(PAUSER_ROLE, superAdmin);
_grantRole(MINTER_ROLE, superAdmin);
_grantRole(BURNER_ROLE, superAdmin);
_grantRole(UPGRADER_ROLE, superAdmin);
}
/**
* @dev Sobreescritura de revokeRole para proteger SUPER_ADMIN_ROLE.
* La revocacion externa del Super Admin queda bloqueada.
* Solo el propio titular puede renunciarlo via renounceRole.
*/
function revokeRole(bytes32 role, address account)
public
override
onlyRole(getRoleAdmin(role))
{
require(
role != SUPER_ADMIN_ROLE,
"ISBE: SUPER_ADMIN_ROLE es irrevocable externamente; use renounceRole"
);
super.revokeRole(role, account);
}
}
2. Circuit Breakers (Mecanismos de Pausa)
Es imperativo que la ejecución de los contratos pueda ser detenida por la administración ante emergencias.
Contratos Custom (Modificador whenOperational)
Si el contrato no permite la herencia directa de Pausable, se debe incluir un modificador personalizado que soporte pausa global y granular por selector de función (msg.sig).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
/**
* @title ISBECustomContract
* @notice Contrato con modificador de pausa personalizado conforme a las
* directrices ISBE para contratos de logica de negocio custom.
* Soporta pausa global y pausa granular por selector de funcion.
*/
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;
// Pausa selectiva por funcion: selector bytes4 => pausado
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);
}
// ---------------------------------------------------------------
// Modificador OBLIGATORIO: debe aplicarse a TODAS las funciones
// externas y publicas que modifiquen estado (state-changing).
// msg.sig contiene el selector de 4 bytes de la funcion activa.
// ---------------------------------------------------------------
modifier whenOperational() {
require(!_globalPause, "ISBE: contrato pausado globalmente");
require(!_functionPaused[msg.sig], "ISBE: funcion pausada");
_;
}
// ---------------------------------------------------------------
// Control de pausa global
// ---------------------------------------------------------------
function pauseGlobal() external onlyRole(PAUSER_ROLE) {
_globalPause = true;
emit GlobalPaused(msg.sender);
}
function unpauseGlobal() external onlyRole(PAUSER_ROLE) {
_globalPause = false;
emit GlobalUnpaused(msg.sender);
}
// ---------------------------------------------------------------
// Control de pausa por funcion individual
// ---------------------------------------------------------------
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);
}
// ---------------------------------------------------------------
// Ejemplo de funcion de negocio protegida
// Replica el patron "whenOperational" en todas las funciones
// state-changing del contrato.
// ---------------------------------------------------------------
function executeOperation(address target, uint256 amount)
external
whenOperational
{
// logica de negocio aqui
}
}
3. Protección de Datos y Privacidad
Dada la naturaleza inmutable de la tecnología blockchain, se exige una auditoría exhaustiva sobre el manejo de los datos.
Reglas de Oro para Datos en ISBE
- Prohibición de PII on-chain: Queda terminantemente prohibido almacenar Información de Identificación Personal (PII) o datos sensibles en texto plano.
- Anonimización Criptográfica: Todo dato privado debe ser tratado mediante técnicas de anonimización (funciones hash) de forma previa a su persistencia.
- Derecho al Olvido: Se recomienda almacenar únicamente hashes on-chain que apunten a datos ("salt") externos cuyo borrado haga irreversible la comprensión del hash.
4. Auditoría de Tipos
Se requiere especial justificación técnica para la utilización de tipos string o matrices de bytes dinámicas en el almacenamiento on-chain, ya que a menudo constituyen una falsa ofuscación de datos que deberían ser tratados fuera de la cadena.
Estándares de Token (Pausable nativo)
Para ERC-20, ERC-721 o ERC-1155, se debe heredar la extensión Pausable (ej. ERC20Pausable) que bloquea transferencias de forma atómica.