Entry Point Analysis: VerdeYield Protocol

Analizado: 2026-06-18 14:30 UTC Scope: src/ (4 contratos) Lenguaje: Solidity ^0.8.20 Herramienta: Análisis manual (Slither no disponible) Enfoque: Solo funciones con cambio de estado (view/pure excluidas)
Auditoría Pre-Deploy
8
Públicas (Sin Restricción)
11
Admin / Owner
2
Manager
4
Guardian / Pauser
2
Keeper
2
Revisión Requerida
4
Solo-Contrato
33
Total Puntos de Entrada
2 funciones requieren revisión inmediata — superficie de ataque crítica trustProtocol() en StrategyManager.sol usa una lista de confianza dinámica con control de acceso ambiguo; y execute() permite llamadas arbitrarias a contratos "de confianza".
Inicializador sin protección detectado VaultCore.initialize() es callable por cualquier dirección antes del primer setup. Si el deploy no se inicializa en la misma transacción, un atacante podría tomar control del vault.
🟢
Puntos de Entrada Públicos (Sin Restricción)
Funciones con cambio de estado accesibles por cualquier dirección — prioridad máxima en análisis de superficie de ataque
8 funciones
Función Archivo Riesgo Notas
initialize(address, address, uint256, uint256) VaultCore.sol:L44 ALTO ⚠ Inicializador sin protección en primera llamada — reentrancia en deploy
deposit(uint256, address) VaultCore.sol:L57 MEDIO Entrada principal de capital — revisar lógica de cálculo de shares (posible manipulación de firstDeposit)
withdraw(uint256, address) VaultCore.sol:L74 MEDIO Salida de capital — ReentrancyGuard aplicado; revisar rounding en cálculo de fees
burn(uint256) GreenToken.sol:L33 BAJO Auto-burn de tokens propios; sin restricción de rol
transfer(address, uint256) GreenToken.sol:L47 BAJO Override ERC20 con blacklist check; patrón correcto
transferFrom(address, address, uint256) GreenToken.sol:L54 BAJO Override ERC20 con blacklist check; patrón correcto
approve(address, uint256) GreenToken.sol:L61 BAJO Override ERC20; revisión correcta de blacklist
voteForPause() EmergencyPause.sol:L60 MEDIO Cualquier pauser registrado puede votar — revisar quién puede ser añadido como pauser
🔐
Puntos de Entrada con Control de Roles
Funciones restringidas a roles específicos — verificar implementación real de cada modificador
19 funciones
Admin / Owner (DEFAULT_ADMIN_ROLE · onlyOwner)
Función Archivo Restricción Notas
setStrategyManager(address) VaultCore.sol:L101 onlyRole(DEFAULT_ADMIN_ROLE) Actualiza estrategia — cambio de dirección crítico
setFeeReceiver(address) VaultCore.sol:L110 onlyRole(DEFAULT_ADMIN_ROLE) Redirige fees a nueva dirección
unpause() VaultCore.sol:L126 onlyRole(DEFAULT_ADMIN_ROLE) Solo admin puede reactivar el vault
addStrategy(address, uint256) StrategyManager.sol:L42 onlyOwner Registra nueva estrategia y la marca como trusted
removeStrategy(address) StrategyManager.sol:L51 onlyOwner Elimina estrategia del pool activo
execute(address, bytes) StrategyManager.sol:L73 onlyOwner ⚠ Llamada arbitraria a contratos trusted — riesgo de delegatecall encubierto
setKeeper(address) StrategyManager.sol:L81 onlyOwner Actualiza dirección del keeper
setSlippageTolerance(uint256) StrategyManager.sol:L88 onlyOwner Max 5% — verificar enforcement en swaps
emergencyDrain(address, uint256) StrategyManager.sol:L94 onlyOwner ⚠ Drena tokens arbitrarios al owner — función de alto riesgo sin timelock
setBlacklist(address, bool) GreenToken.sol:L38 onlyRole(DEFAULT_ADMIN_ROLE) Puede congelar fondos de cualquier usuario
setMaxSupply(uint256) GreenToken.sol:L45 onlyRole(DEFAULT_ADMIN_ROLE) Puede aumentar suministro máximo sin límite superior
Manager (MANAGER_ROLE)
Función Archivo Restricción Notas
rebalance(uint256) VaultCore.sol:L95 onlyRole(MANAGER_ROLE) Redistribuye capital entre estrategias
setFees(uint256, uint256) VaultCore.sol:L104 onlyRole(MANAGER_ROLE) Max performanceFee 30%, withdrawalFee 5% — verificar checks
Guardian / Pauser
Función Archivo Restricción Notas
emergencyWithdraw(address, address) VaultCore.sol:L89 hasRole(GUARDIAN_ROLE) Retiro forzado a receptor arbitrario — verificar que receiver sea controlado
pause() VaultCore.sol:L122 onlyRole(GUARDIAN_ROLE) Pausa el vault instantáneamente
triggerEmergencyPause() EmergencyPause.sol:L46 onlyGuardian (guardian || admin) Pausa global del protocolo
addPauser(address) EmergencyPause.sol:L70 onlyAdmin Añade votantes al mecanismo de pausa colectiva
Keeper (KEEPER_ROLE · msg.sender == keeper)
Función Archivo Restricción Notas
harvest() VaultCore.sol:L89 hasRole(KEEPER_ROLE) Llama a strategy.harvest() y actualiza totalAssets
harvest() StrategyManager.sol:L59 onlyKeeper (keeper || owner) Itera estrategias activas con llamada dinámica — revisar reentrancy con strategies externas
Minter / Burner (GreenToken)
Función Archivo Restricción Notas
mint(address, uint256) GreenToken.sol:L27 MINTER_ROLE Limitado por maxSupply — quién tiene este rol importa
burnFrom(address, uint256) GreenToken.sol:L39 BURNER_ROLE Quema tokens de cualquier dirección sin aprobación
Admin (EmergencyPause — 2-step transfer)
Función Archivo Restricción Notas
liftEmergencyPause() EmergencyPause.sol:L52 onlyAdmin Solo admin puede desactivar pausa global
transferAdmin(address) EmergencyPause.sol:L77 onlyAdmin Patrón 2-step — correcto
updateGuardian(address) EmergencyPause.sol:L89 onlyAdmin Reemplaza guardian sin timelock
resetVotes(address[]) EmergencyPause.sol:L96 onlyAdmin Admin puede resetear votos de pausa — posible abuso para bloquear mecanismo de pausa colectiva
Pending Admin (EmergencyPause)
Función Archivo Restricción Notas
acceptAdmin() EmergencyPause.sol:L84 msg.sender == pendingAdmin Completa la transferencia de admin — verificar que pendingAdmin no sea address(0)
🔍
Restricción Ambigua — Revisión Manual Requerida
Funciones con patrones de control de acceso que requieren verificación de la implementación real del modificador
2 funciones
Función Archivo Patrón detectado Motivo de revisión
trustProtocol(address, bool) StrategyManager.sol:L99 require(trusted[msg.sender] || msg.sender == owner) ⚠ Lista de confianza dinámica: cualquier protocolo ya trusted puede añadir nuevos. Posible escalada de privilegios — verificar si trusted y trustedProtocols son el mismo mapping.
execute(address, bytes) StrategyManager.sol:L73 onlyOwner + trustedProtocols[target] ⚠ Función de ejecución arbitraria: owner puede llamar a cualquier función en contratos "trusted". Si trustProtocol() tiene vulnerabilidad, un atacante puede añadir contratos maliciosos y luego ejecutar código arbitrario.
🔗
Solo-Contrato (Puntos de Integración Interna)
Callbacks y hooks que solo deberían ser llamados por contratos externos específicos — definen fronteras de confianza
4 funciones
Función Archivo Llamante esperado Verificación
executeOperation(address[], uint256[], uint256[], address, bytes) VaultCore.sol:L130 AAVE Flash Loan Pool tx.origin != msg.sender + msg.sender == strategyManager — verificar dirección AAVE en mainnet
rebalance(uint256) StrategyManager.sol:L69 VaultCore.sol onlyVault — correcto; verificar que vault no pueda ser reemplazado en el constructor
uniswapV3SwapCallback(int256, int256, bytes) StrategyManager.sol:L103 Uniswap V3 Pool tx.origin != msg.sender + trustedProtocols[msg.sender] — depende de que la lista de trusted sea correcta
executeOperation(address, uint256, uint256, address, bytes) StrategyManager.sol:L113 AAVE Flash Loan Pool tx.origin != msg.sender — falta validar dirección del pool AAVE
📁
Archivos Analizados
Todos los contratos en scope con su conteo de puntos de entrada
src/VaultCore.sol
12 entry points
Solidity ^0.8.20
src/StrategyManager.sol
12 entry points
⚠ Revisión urgente
src/GreenToken.sol
7 entry points
ERC20 + Roles
src/EmergencyPause.sol
7 entry points
Multi-role