Token Security Audit Report

YieldNest Protocol — ERC20 Implementation + Vault Integration
AUDIT-ID: YN-2026-0014  /  acc2f2df
RISK: HIGH
Protocolo
YieldNest Protocol
Fecha Auditoría
2026-06-16
Plataforma
Ethereum / Solidity 0.8.20
Tokens Analizados
$NEST (impl.) + 5 externos
Framework
Trail of Bits Checklist
Estado
● NO APTO PARA LAUNCH

Executive Summary

2 Critical
4 High
5 Medium
3 Low / Info

⚠ Top Concerns — Bloquean el Launch

[CRITICAL] YieldVault.deposit() usa IERC20.transferFrom sin SafeERC20 — USDT (sin return value) causará fallos silenciosos en producción
[CRITICAL] Fee-on-transfer (PAXG integrado): el vault acredita amount pero recibe amount - fee — vector de drenado del protocolo
[HIGH] NestToken: owner puede blacklistear cualquier dirección unilateralmente, incluyendo el propio vault — riesgo de fondos atrapados
[HIGH] Proxy upgradeable sin timelock: owner puede cambiar lógica del contrato sin aviso previo — riesgo de rug pull
1
General Considerations
PARCIAL
Equipo público e identificado (LinkedIn verificado)
Canal de contacto de seguridad: security@yieldnest.fi
Sin auditoría externa previa — primer despliegue a mainnet sin audit independiente
Sin bug bounty program establecido
Docs técnicas incompletas — no hay NatSpec en funciones críticas
MEDIUM Sin auditoría independiente pre-launch
Lanzar a mainnet sin audit externo eleva el riesgo de pérdida de fondos de usuarios. Acción: Contratar auditoría con Code4rena, Sherlock o similar antes del lanzamiento.
2
Contract Composition & Complexity
PASS
LOC Total
823 líneas
Complejidad ciclomática máx
11 (deposit())
Profundidad herencia
4 niveles
Solidity 0.8.20 — overflow protection nativo, sin SafeMath manual
Sin bloques unchecked en rutas críticas de contabilidad
Dirección única de contrato (sin proxy multi-address)
YieldVault.deposit() tiene complejidad 11 — refactorizar en subfunciones
ReentrancyGuard aplicado en withdraw() ✓
3
Owner Privileges & Centralization Risks
FAIL
HIGH Proxy upgradeable sin timelock — riesgo de rug pull
Archivo: NestToken.sol — TransparentUpgradeableProxy
El ProxyAdmin no tiene timelock. El owner puede actualizar la lógica del contrato al instante, sin previo aviso.
Riesgo: Upgrade malicioso puede drenar fondos del vault en una transacción.
Fix: Añadir TimelockController de OpenZeppelin con mínimo 48h delay.
MEDIUM Mint con cap correcto, pero minting ilimitado en velocidad
El cap de 100M tokens existe, pero no hay rate limiting. El owner puede mintear todo el supply en un solo bloque.
Fix: Añadir vesting schedule o mint rate limit por período.
HIGH Blacklist sin governance — owner puede bloquear el vault
function addToBlacklist(address account) external onlyOwner {
    blacklist[account] = true; // puede blacklistear address(YieldVault)
}
Si el owner blacklistea la dirección del vault, todos los usuarios quedan con fondos atrapados. Sin mecanismo de apelación.
Fix: Excluir contratos whitelisteados de blacklist, o requerir multisig para blacklist.
4
ERC20 Conformity — $NEST Token
PASS
transfer() retorna bool correctamente
transferFrom() retorna bool correctamente
name(), symbol(), decimals() presentes (18 decimales)
increaseAllowance/decreaseAllowance implementados (race condition mitigada)
slither-check-erc: TOTALMENTE CONFORME
Pausable activo — en estado paused, todos los transfers fallan (incluyendo DEXes)
5
Weird ERC20 Patterns — Análisis Integración
2 CRITICAL
CRITICAL Pattern 7.2: Missing Return Values — USDT no retorna bool
Archivo: YieldVault.sol:156
// VULNERABILIDAD: USDT no retorna bool en transferFrom
IERC20(token).transferFrom(msg.sender, address(this), amount);
// No hay chequeo de valor de retorno
_creditShares(msg.sender, token, amount); // se ejecuta siempre
Impacto: Si la llamada USDT falla silenciosamente, el usuario recibe shares sin haber depositado tokens.
Explotable: Sí — vaciado del vault por depósitos falsos.
Fix: Usar SafeERC20.safeTransferFrom() de OpenZeppelin en todas las llamadas.
CRITICAL Pattern 7.3: Fee-on-Transfer — PAXG cobra comisión en cada transfer
Archivo: YieldVault.sol:170-174
// VULNERABILIDAD: amount acreditado != amount recibido (PAXG cobra ~0.02%)
uint256 balanceBefore = IERC20(token).balanceOf(address(this));
token.transferFrom(msg.sender, address(this), amount);
_creditShares(msg.sender, token, amount); // ERROR: debería ser balanceAfter - balanceBefore
Impacto: El vault acredita más shares de las que corresponden. Usuario puede retirar amount pero el vault solo recibió amount × 0.9998. Pérdida acumulada drena el vault.
Fix: Calcular shares a partir del delta de balance real recibido.

ANÁLISIS DE 24 PATRONES WEIRD-TOKEN EN TOKENS INTEGRADOS:

7.1 Reentrant Calls (ERC777)
✓ No presente
7.2 Missing Return Values
✗ CRÍTICO (USDT)
7.3 Fee on Transfer
✗ CRÍTICO (PAXG)
7.4 Balance Outside Transfers
✓ No presente
7.5 Upgradable Token
⚠ HIGH (USDC, USDT)
7.6 Flash Mintable
⚠ MEDIUM (DAI)
7.7 Blocklist
⚠ MEDIUM (USDC)
7.8 Pausable
✓ Manejado
7.9 Approval Race
⚠ LOW (USDT, KNC)
7.10 Revert on Zero Value
✓ No presente
7.11 Low Decimals
⚠ LOW (USDC:6, WBTC:8)
7.12 Large Approval Revert
✓ No afecta
6
Token Integration Safety — Matriz de Riesgo
FAIL
Token Dirección Patrones Weird SafeERC20 Balance Check Riesgo
USDT 0xdac17f...ec7 No return Upgradable NO NO CRITICAL
USDC 0xa0b869...b48 Upgradable Blocklist NO NO HIGH
DAI 0x6b1754...0f Flash mint NO NO MEDIUM
WBTC 0x2260fa...99 8 decimals NO MEDIUM
PAXG 0x45804...78 Fee-on-xfer NO NO CRITICAL
7
Recomendaciones Priorizadas
14 ACCIONES
P0 · Crítico
Migrar todas las llamadas a SafeERC20 Reemplazar IERC20(token).transfer/transferFrom por SafeERC20.safeTransfer/safeTransferFrom en todo YieldVault.sol. Esto resuelve USDT y todos los tokens sin return value.
P0 · Crítico
Corregir contabilidad de shares para fee-on-transfer En deposit(), calcular shares como balanceAfter - balanceBefore, no como amount. Aplica a PAXG y cualquier token con comisión futura.
P1 · Alto
Añadir TimelockController al proxy Implementar TimelockController de OZ con mínimo 48h delay. Anunciar cambios con al menos 24h de antelación pública (governance forum).
P1 · Alto
Proteger vault de blacklist Añadir require(account != address(yieldVault), "Cannot blacklist vault") en addToBlacklist(), o migrar blacklist a multisig 2/3 con timelock.
P2 · Medio
Implementar token allowlist Crear un registro de tokens aprobados con sus propiedades (hasFee, lowDecimals, upgradable). Rechazar integraciones de tokens no auditados.
P2 · Medio
Añadir NatSpec completo Documentar todas las funciones con @param, @return y @dev. Requerido para auditoría externa y para Slither/echidna property generation.
P3 · Bajo
Configurar bug bounty en Immunefi Establecer programa de recompensas con mínimo $10k para críticos antes del lanzamiento.
8
Compliance Checklist — Trail of Bits
7/10 CATEGORÍAS
1. General Considerations — PARCIAL
2. Contract Composition — PASS
3. Owner Privileges — FAIL (sin timelock)
4. ERC20 Conformity ($NEST) — PASS
5. ERC20 Extension Risks — FAIL (fee-xfer)
6. Token Scarcity Analysis — PENDIENTE
7. Weird ERC20 Patterns — FAIL (2 críticos)
8. Token Integration Safety — FAIL (SafeERC20)
9. ERC721 Conformity — N/A (no aplica)
10. ERC721 Common Risks — N/A (no aplica)