Documento de Objetivos de Revisión
Objetivo Principal
Verificar que ningún atacante puede vaciar los colaterales del protocolo mediante manipulación de oráculos o liquidaciones fraudulentas
Preocupación Crítica #1
Liquidaciones parciales — ¿puede un vault quedar en estado inconsistente con health factor < 1 sin ser liquidado?
Preocupación Crítica #2
Ventana entre actualizaciones Chainlink + Pyth: manipulación de precios durante el gap de latencia entre oráculos
Peor Escenario
Flash loan → manipula oráculo → liquida vaults legítimos → drena 100% del TVL del protocolo en una transacción
Reporte Análisis Estático — Slither
0
High
0
Medium
3
Low
7
Info
LOW
Reentrancy guard faltante en función legacy
src/FeeAccumulator.sol:203 · collect()
✓ Resuelto
LOW
División antes de multiplicación (precision loss)
src/VaultManager.sol:341 · _healthFactor()
✓ Resuelto
INFO
Variable de estado leída en loop (gas)
src/CollateralRegistry.sol:89 · listAssets()
~ Aceptado
slither . --exclude-dependencies
Ejecutado: 2026-07-01 · Estado: LIMPIO (críticos/medium resueltos)
Cobertura de Tests — forge coverage
89%
Statements
85%
Branches
96%
Functions
VaultManager.sol
93%
LiquidationEngine.sol
91%
OracleAdapter.sol
78%
StablecoinMinter.sol
95%
CollateralRegistry.sol
100%
FeeAccumulator.sol
88%
Governance.sol
72%
EmergencyPause.sol
75%
PriceGuard.sol
100%
Alcance del Código
| Archivo | LOC | Scope |
|---|---|---|
| VaultManager.sol | 612 | IN SCOPE |
| LiquidationEngine.sol | 489 | IN SCOPE |
| StablecoinMinter.sol | 334 | IN SCOPE |
| Governance.sol | 267 | IN SCOPE |
| CollateralRegistry.sol | 178 | IN SCOPE |
| OracleAdapter.sol | 201 | IN SCOPE |
| FeeAccumulator.sol | 145 | IN SCOPE |
| PriceGuard.sol | 89 | IN SCOPE |
| EmergencyPause.sol | 56 | IN SCOPE |
| lib/ (OpenZeppelin) | — | OUT |
| test/ | — | OUT |
Total en scope: 3.271 LOC · 9 contratos · Commit:
a7f3c89 · Branch: audit/trail-of-bits-jul26
Actores y Privilegios
ADMIN
Protocol Owner
Pause global, upgrade proxies, cambiar parámetros críticos (sin timelock en v2 — PREOCUPACIÓN ACTIVA)
TRUSTED
Keeper / Liquidador
Puede iniciar liquidaciones parciales. Whitelistado pero sin stake de seguridad en v2
TRUSTED
Oráculo (Chainlink / Pyth)
Provee precios. Sistema confía en ambos; sin circuit breaker si divergen > 5%
USER
Borrower / Depositor
Puede depositar colateral, acuñar VPUSD, repagar deuda, retirar colateral
EXTERNAL
Governance Token Holder
Voto en propuestas de cambio de parámetros (timelock 48h para cambios de riesgo)
MULTISIG
Emergency Multisig (3/5)
Solo puede pausar el protocolo. No puede upgradearlo ni mover fondos directamente
Timeline de Preparación
2026-06-14
Inicio preparación
Definición de objetivos y áreas de preocupación
2026-06-20
Análisis estático ejecutado
Slither + Semgrep · 2 issues high resueltos, 3 low triageados
2026-06-28
Cobertura de tests mejorada
68% → 89% · 47 nuevos tests añadidos (liquidaciones parciales, edge cases oráculos)
2026-07-01
Código muerto eliminado
3 funciones deprecadas, 2 eventos sin usar, 1 librería obsoleta
2026-07-03
Versión congelada
Tag v2.0.0-audit · Branch audit/trail-of-bits-jul26 · Commit a7f3c89
2026-07-07
Paquete entregado a Trail of Bits
Envío de este documento + repositorio + acceso al repo privado
2026-07-14
Inicio de auditoría
Kickoff call con el equipo de Trail of Bits · 4 semanas
Diagrama: Flujo de Liquidación Parcial
┌─────────────────────────────────────────────────────┐
│ FLUJO LIQUIDACIÓN PARCIAL │
└─────────────────────────────────────────────────────┘
Keeper ──── checkLiquidatable(vaultId) ────▶ VaultManager
│
┌──────────────────────────────┘
▼
OracleAdapter ──▶ Chainlink.latestAnswer()
│ └──▶ Pyth.getPrice()
│ │
▼ ▼
price = TWAP(chainlink, pyth)
│
▼
healthFactor = (colateral × price) / deuda
│
┌───────────┴────────────┐
▼ ▼
HF < 1.0 HF >= 1.0
│ NO LIQUIDABLE
▼
LiquidationEngine
├── seizeAmount = deuda × liquidationBonus (5%)
├── repayDebt() → StablecoinMinter.burn(VPUSD)
└── transferCollateral() → Keeper recibe colateral
⚠ ZONA DE RIESGO: Entre step OracleAdapter y healthFactor
un flashloan puede manipular el precio del colateral
Preguntas para el Equipo Auditor
- 1 ¿Puede el TWAP de dos oráculos con latencias distintas producir un precio manipulable durante el intervalo de bloque entre ambas actualizaciones?
-
2
¿El health factor calculado en
_healthFactor()puede dar falso positivo de liquidación si hay precision loss por el orden división/multiplicación? - 3 ¿Un vault con multi-colateral puede quedar en estado inconsistente si una liquidación parcial falla en el transfer de uno de los activos?
- 4 ¿Las funciones admin del owner sin timelock representan un riesgo de centralización inaceptable para el TVL proyectado de $50M?
- 5 ¿Es posible acuñar VPUSD y quemarlo en la misma transacción para explotar el invariante del minter?
-
6
¿El circuit breaker
PriceGuardpuede ser bypasseado si los dos oráculos se mueven en la misma dirección dentro del umbral?
Glosario del Dominio
VPUSD
Stablecoin emitido por VaultPay, colateralizado al 150% mínimo. Soft-pegged a USD.
Vault
Posición individual de un usuario: contiene colaterales depositados y deuda VPUSD acuñada.
Health Factor (HF)
Ratio (valor_colateral / deuda). HF < 1.0 activa liquidación. Calculado en tiempo real.
Liquidación Parcial
Solo se liquida la porción necesaria para restaurar HF ≥ 1.2, no el vault completo.
Keeper
Bot externo whitelistado que ejecuta liquidaciones a cambio del liquidationBonus (5%).
LTV (Loan-to-Value)
Máxima deuda permitida como % del colateral. Depende del activo: ETH=80%, WBTC=75%, altcoins=60%.
TWAP Oracle
Precio calculado como media ponderada de Chainlink y Pyth, ponderada por tiempo de actualización.
CollateralFactor
Parámetro de riesgo por activo (0-100%), multiplicado por el precio para el cálculo del HF.
seizeAmount
Colateral transferido al liquidador: repayAmount + bonus. Calculado en OracleAdapter units.
PriceGuard
Circuit breaker: bloquea operaciones si el precio de un activo cae/sube >30% en un bloque.
NatSpec
Formato de documentación de Solidity (@notice, @dev, @param, @return). Cubierto al 100% en scope.
Invariant
Propiedad que debe ser siempre verdadera. Ej: suma(deudas) ≤ suma(colaterales × precio × CF).
Instrucciones de Build Verificadas (entorno limpio)
Prerequisitos
# Versiones exactas verificadas
Foundry: forge 0.2.0 (foundry-rs/foundry)
Node.js: v20.14.0
Git: 2.45.1
OS: Linux/macOS (ARM64 / x86_64)
# Herramientas análisis (opcional)
Slither: 0.10.3
Semgrep: 1.72.0
Foundry: forge 0.2.0 (foundry-rs/foundry)
Node.js: v20.14.0
Git: 2.45.1
OS: Linux/macOS (ARM64 / x86_64)
# Herramientas análisis (opcional)
Slither: 0.10.3
Semgrep: 1.72.0
Setup paso a paso
git clone https://github.com/vaultpay-fi/vaultpay-v2
cd vaultpay-v2
git checkout audit/trail-of-bits-jul26
forge install
forge build
forge test --gas-report
# ✓ 134 tests · 0 failures · Build OK
cd vaultpay-v2
git checkout audit/trail-of-bits-jul26
forge install
forge build
forge test --gas-report
# ✓ 134 tests · 0 failures · Build OK