Skip to main content

lending

Auto-loaded by defi-auditor agent during Phase 2 when analyzing lending protocols. Provides patterns for: liquidation logic, interest rate models, collateral management, health factor calculations, bad debt handling. Core vulnerabilities: self-liquidation, precision loss, oracle manipulation, collateral factor attacks.

Jump to install

Source facts

Repository
BitterSecurity/Vigilo
Last source activity
February 2, 2026 at 07:15
Detected SKILL.md language
English
Stars
66
Forks
17

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
lending
description
Auto-loaded by defi-auditor agent during Phase 2 when analyzing lending protocols. Provides patterns for: liquidation logic, interest rate models, collateral management, health factor calculations, bad debt handling. Core vulnerabilities: self-liquidation, precision loss, oracle manipulation, collateral factor attacks.
user-invocable
false
# Lending Protocol Patterns This skill provides comprehensive knowledge for auditing DeFi lending protocols. ## Core Lending Concepts | Concept | Description | |---------|-------------| | Collateral Factor | Max borrow % against collateral | | Liquidation Threshold | Health level triggering liquidation | | Utilization Rate | Borrowed / Total Supplied | | Interest Rate | Function of utilization | | Health Factor | Collateral value / Debt value | --- ## Why Lending Protocol Attacks Happen (Root Causes) ### Root Cause 1: Health Factor Calculation Timing The fundamental vulnerability: health factors computed from stale or cached data instead of real-time prices. ```solidity // VULNERABLE: Cached health factor mapping(address => uint256) public healthFactors; function liquidate(address user) external { // Uses stale cached value require(healthFactors[user] < 1e18); // Actual health factor may be different! } // SECURE: Compute in real-time function liquidate(address user) external { uint256 collateralValue = getCollateralValue(user); // Real-time oracle uint256 debtValue = getDebtValue(user); uint256 healthFactor = collateralValue * 1e18 / debtValue; require(healthFactor < 1e18); } ``` **Attacker's view**: "The protocol checks a stale health factor. I can manipulate prices between the check and liquidation, or liquidate users who are actually safe." ### Root Cause 2: Liquidation Incentive Misconfiguration Liquidation bonus set higher than the safety margin, making self-liquidation profitable. ```solidity // VULNERABLE: Bonus exceeds safety margin uint256 constant MAX_LTV = 80e16; // 80% uint256 constant LIQUIDATION_BONUS = 15e16; // 15% // Attacker borrows at 80% LTV, self-liquidates with 15% bonus // Net profit: 15% - (100% - 80%) = -5%... wait, this is a loss // But if bonus > (100% - LTV), it's profitable! uint256 constant LIQUIDATION_BONUS = 25e16; // 25% (DANGEROUS!) // Profit: 25% - 20% = 5% free money ``` **Attacker's view**: "I borrow at max LTV, wait for any price movement, then self-liquidate and pocket the bonus." ### Root Cause 3: Interest Precision Loss Small amounts round to zero during interest accrual, allowing dust attacks or precision exploits. ```solidity // VULNERABLE: Interest rounds to 0 uint256 interest = principal * rate / SECONDS_PER_YEAR; // If principal * rate < SECONDS_PER_YEAR, interest = 0 // Example: principal = 100 wei, rate = 1e16 (1%) // interest = 100 * 1e16 / 31536000 = 0 (rounds down) // SECURE: Use high-precision accumulator uint256 interestAccumulator; // Ray (1e27) precision uint256 newAccumulator = oldAccumulator + (rate * 1e27 / SECONDS_PER_YEAR); ``` **Attacker's view**: "I can deposit tiny amounts that accrue zero interest, or exploit rounding to avoid paying interest on small borrows." ### Root Cause 4: Collateral Valuation Trust Collateral value depends entirely on oracle accuracy. Stale, manipulated, or incorrect oracles break the entire protocol. ```solidity // VULNERABLE: Single oracle, no staleness check function getCollateralValue(address user) public view returns (uint256) { uint256 price = oracle.getPrice(collateralToken); // Could be stale! return userBalance * price / 1e18; } // SECURE: Validate oracle freshness function getCollateralValue(address user) public view returns (uint256) { (uint256 price, uint256 timestamp) = oracle.getPriceWithTimestamp(collateralToken); require(block.timestamp - timestamp <= MAX_STALENESS); return userBalance * price / 1e18; } ``` **Attacker's view**: "If I can manipulate the oracle or use a stale price, I can inflate collateral value and borrow more than safe." ### Root Cause 5: Bad Debt Socialization Gap When liquidation fails to recover full debt, bad debt accumulates and is socialized across remaining suppliers, creating insolvency. ```solidity // VULNERABLE: No bad debt handling function liquidate(address user) external { uint256 collateralValue = getCollateralValue(user); uint256 debtValue = getDebtValue(user); // If collateral < debt, bad debt remains! // Suppliers lose money } // SECURE: Handle underwater positions function liquidate(address user) external { uint256 collateralValue = getCollateralValue(user); uint256 debtValue = getDebtValue(user); if (collateralValue < debtValue) { uint256 badDebt = debtValue - collateralValue; // Socialize or use insurance fund insuranceFund -= min(badDebt, insuranceFund); } } ``` **Attacker's view**: "If I can create a position where collateral < debt, the protocol becomes insolvent and I profit from the socialized loss." --- ## Liquidation Vulnerabilities ### 1. Profitable Self-Liquidation **Root Cause**: Root Cause 2 (Liquidation Incentive Misconfiguration) **Vulnerable Pattern:** ```solidity // DANGEROUS: Liquidator bonus too high uint256 constant LIQUIDATION_BONUS = 15e16; // 15% function liquidate(address user, uint256 repayAmount) external { // Attacker can: // 1. Borrow at 80% LTV // 2. Wait for tiny price drop // 3. Self-liquidate with 15% bonus // = Free 15% - (100% - 80%) = Net profit! } ``` **Check:** Liquidation bonus should be less than (100% - Max LTV). ### 2. Liquidation Cascade ```solidity // When one liquidation triggers another // Large position liquidated → price drops → more liquidations // Mitigations: // - Gradual liquidation (partial) // - Circuit breakers // - Liquidation delays ``` ### 3. Bad Debt Accumulation **Root Cause**: Root Cause 5 (Bad Debt Socialization Gap) **Vulnerable Pattern:** ```solidity // DANGEROUS: No bad debt handling function liquidate(address user) external { // If collateral < debt after liquidation // Bad debt remains in protocol // Eventually becomes insolvent } // SECURE: Handle underwater positions function liquidate(address user) external { uint256 collateralValue = getCollateralValue(user); uint256 debtValue = getDebtValue(user); if (collateralValue < debtValue) { // Socialize bad debt or use insurance fund uint256 badDebt = debtValue - collateralValue; insuranceFund -= min(badDebt, insuranceFund); } } ``` --- ## Interest Rate Model ### Standard Jump Rate Model ```solidity contract JumpRateModel { uint256 public baseRate; // Rate at 0% utilization uint256 public multiplier; // Rate increase per utilization uint256 public jumpMultiplier; // Rate increase above kink uint256 public kink; // Utilization % where jump occurs function getBorrowRate(uint256 cash, uint256 borrows) public view returns (uint256) { uint256 utilization = borrows * 1e18 / (cash + borrows); if (utilization <= kink) { return baseRate + utilization * multiplier / 1e18; } else { uint256 normalRate = baseRate + kink * multiplier / 1e18; uint256 excessUtil = utilization - kink; return normalRate + excessUtil * jumpMultiplier / 1e18; } } } ``` ### Interest Rate Vulnerabilities **1. Rate Manipulation** ```solidity // Attacker can manipulate utilization // 1. Flash loan large amount // 2. Deposit → lower utilization → lower rates // 3. Borrow at low rate // 4. Withdraw and repay flash loan ``` **2. Precision Loss** **Root Cause**: Root Cause 3 (Interest Precision Loss) ```solidity // DANGEROUS: Interest rounds to 0 for small amounts uint256 interest = principal * rate / SECONDS_PER_YEAR; // If principal * rate < SECONDS_PER_YEAR, interest = 0 // SOLUTION: Accumulate interest with high precision uint256 interestAccumulator; // Ray (1e27) precision ``` --- ## Collateral Vulnerabilities ### 1. Collateral Factor Changes ```solidity // DANGEROUS: Instant collateral factor reduction function setCollateralFactor(address token, uint256 newFactor) external { collateralFactors[token] = newFactor; // Existing borrowers may become instantly liquidatable! } // SECURE: Grace period or gradual reduction function setCollateralFactor(address token, uint256 newFactor) external { require(newFactor >= collateralFactors[token] - MAX_DECREASE); pendingFactors[token] = newFactor; factorEffectiveTime[token] = block.timestamp + TIMELOCK; } ``` ### 2. Collateral Oracle Manipulation **Root Cause**: Root Cause 4 (Collateral Valuation Trust) ```solidity // Collateral valued using manipulable oracle // Attacker inflates collateral value → borrows more → profits // See oracle vulnerability patterns ``` ### 3. Toxic Collateral ```solidity // Collateral that can't be liquidated: // - Pausable tokens // - Blacklistable tokens // - Low liquidity tokens // Mitigation: Whitelist collateral carefully ``` --- ## Borrowing Vulnerabilities ### 1. Borrow Cap Bypass ```solidity // VULNERABLE: Cap checked only on new borrows function borrow(uint256 amount) external { require(totalBorrowed + amount <= borrowCap); // Interest accrual can push over cap! } // Check on any state-changing operation ``` ### 2. Same-Block Borrow-Repay ```solidity // EXPLOIT: Borrow and repay in same block // No interest accrued = free leverage function borrow(uint256 amount) external { require(lastBorrowBlock[msg.sender] < block.number); lastBorrowBlock[msg.sender] = block.number; // ... } ``` --- ## Health Factor Calculations ### Standard Formula ``` Health Factor = (Collateral Value × LTV) / Debt Value Health Factor > 1: Safe Health Factor < 1: Liquidatable ``` ### Vulnerabilities **1. Stale Health Factor** **Root Cause**: Root Cause 1 (Health Factor Calculation Timing) ```solidity // DANGEROUS: Cached health factor mapping(address => uint256) public healthFactors; // Health factors become stale as prices change // Always compute in real-time ``` **2. Rounding Direction** ```solidity // SECURE: Round health factor DOWN (safer) uint256 healthFactor = collateralValue * 1e18 / debtValue; // Round debt UP when calculating uint256 debt = (borrowed * (1e18 + interest) + 1e18 - 1) / 1e18; ``` --- ## Common AAVE/Compound Patterns ### Reserve Factor ```solidity // Protocol takes cut of interest uint256 interestToProtocol = interest * reserveFactor / 1e18; uint256 interestToSuppliers = interest - interestToProtocol; ``` ### Supply Index ```solidity // Track earnings per token // supplyIndex increases as interest accrues uint256 newIndex = oldIndex + (interest * 1e18 / totalSupply); // User earnings = balance * (currentIndex - userIndex) / 1e18 ``` --- ## Lending Audit Checklist ### Liquidation - [ ] Liquidation bonus < (100% - max LTV) - [ ] Bad debt handling exists - [ ] Partial liquidation supported - [ ] Liquidation can't be blocked (pausable tokens) - [ ] Self-liquidation not profitable ### Interest - [ ] Interest accrues correctly over time - [ ] No precision loss on small amounts - [ ] Rate manipulation resistance - [ ] Compound interest handled ### Collateral - [ ] Oracle is manipulation-resistant - [ ] Collateral factor changes have timelock - [ ] Collateral whitelist is reasonable - [ ] Handles rebasing/weird tokens properly ### Health Factor - [ ] Computed in real-time - [ ] Rounding favors protocol safety - [ ] Handles all edge cases (0 debt, 0 collateral) ### Access Control - [ ] Only authorized can change parameters - [ ] Emergency pause exists - [ ] Parameter bounds enforced ## Severity Classification ### Critical - Self-liquidation profitable - Bad debt not handled - Oracle manipulation allows draining ### High - Interest precision loss - Collateral factor instant changes - Same-block manipulation ### Medium - Missing borrow caps - Liquidation can be blocked - Rate model edge cases --- ## Rationalization Table | Root Cause | Historical Exploit | Detection Method | Severity | Mitigation | |-----------|-------------------|------------------|----------|-----------| | Health Factor Calculation Timing | Stale price liquidations (Aave v1 oracle failures) | Check if health factor computed real-time from oracle | Critical | Always compute health factor in real-time, validate oracle freshness | | Liquidation Incentive Misconfiguration | Self-liquidation profit (Compound liquidation bonus > safety margin) | Verify: bonus < (100% - max LTV) | Critical | Set liquidation bonus ≤ (100% - max LTV), audit parameter changes | | Interest Precision Loss | Dust attack exploits (small amounts accrue zero interest) | Check interest calculation for rounding to zero | High | Use high-precision accumulators (ray/wad), test with small amounts | | Collateral Valuation Trust | Oracle manipulation attacks (Euler oracle dependency) | Audit oracle source, check staleness, validate multi-source | Critical | Use manipulation-resistant oracles, implement staleness checks, multi-oracle fallback | | Bad Debt Socialization Gap | Protocol insolvency (Euler bad debt cascade) | Check if collateral < debt is handled | Critical | Implement bad debt handling, insurance fund, or liquidation guarantees |
View on GitHub