Protocol Type Trigger lending (detected when recon finds liquidate|borrow|repay|collateral|lend|loan|LTV|healthFactor|interestRate|debtToken) - Inject Into Breadth agents, depth...
Sections 3, 3b, 3c: depth-token-flow (liquidation flows, bad debt socialization)
Section 6: depth-external (oracle dependency for pricing)
When This Skill Activates
Skills relacionados
Recon classifies protocol as lending type based on indicators: liquidate, borrow, repay, collateral, lend, loan, LTV, healthFactor, interestRate, debtToken, reserve, utilizationRate, debtShare.
This skill adds lending-specific checks that the general ECONOMIC_DESIGN_AUDIT does not cover.
1. Health Factor Boundary Analysis
1a. Health Factor Computation
Identify the health factor (HF) formula and trace each input:
What is the formula? (typically: HF = (collateralValue * liquidationThreshold) / debtValue)
How is collateralValue derived? (oracle price * collateral amount * collateral factor)
How is debtValue derived? (oracle price * borrow amount including accrued interest)
Is interest included in the HF check at the moment of the check, or from a stale snapshot?
Substitute boundary values into the formula:
State
HF Value
Expected Behavior
Actual?
HF = 1.0 exactly
Liquidation threshold - which side?
HF = 1.0 + 1 wei
Should NOT be liquidatable
HF = 1.0 - 1 wei
Should be liquidatable
HF after max interest accrual
Worst case between check intervals
HF with dust collateral
Liquidation gas > reward?
1b. Check-to-Execution Consistency
Between HF check and liquidation execution: can any state change (oracle update, interest accrual, collateral deposit) alter the HF?
If a user's borrow increases their debt: is HF re-checked atomically or can they borrow below HF=1.0?
Grep every exit-path (withdraw, transfer, finalize_transfer) for the SAME health predicate used at borrow entry. If a DIFFERENT or WEAKER predicate is used on any exit-path, document both predicate names and flag the asymmetry.
1c. Dust Position Economics
Grep deposit/borrow entry points for minimum amount/value checks (min_borrow, min_deposit, require amount >= X). If NONE found → document "NO_MINIMUM_POSITION" as a finding: attackers can Sybil-create dust positions that cost more to liquidate than they're worth, accumulating bad debt.
If a minimum exists at creation: grep partial-repay path for the same check. If missing → dust positions can be created by repaying down to near-zero.
For protocols that use transaction guards or post-transaction hooks with conditional
enforcement checks (e.g., post-transaction guard/hook triggered by selector or return value):
Enumerate ALL function selectors the guard/hook handles
For each selector: does the operation affect HF? (changes collateral amount,
debt amount, collateral factor, efficiency/isolation mode, reserve configuration)
For each HF-affecting selector: does the guard/hook trigger the
post-transaction HF enforcement check?
Flag any HF-affecting selector where post-tx enforcement is skipped
If any step is out of order or conditional: flag for depth review
2c. Precision Loss
Compound interest over many small intervals vs one large interval: is the result equivalent?
For utilization-rate-based interest: does the utilization rate use pre- or post-operation values?
Over 365 days of per-second compounding: does accumulated precision error become material? (compute concrete drift using the protocol's actual rate model constants)
2d. Pause-Interest Interaction
During pause: does interest continue to accrue?
If interest accrues during pause BUT repayment is blocked: borrowers accumulate debt they cannot repay, potentially becoming liquidatable upon unpause
If interest does NOT accrue during pause: lenders lose yield for the paused period
Is there a fallback path (escrow, protocol seizure) for blocklisted positions?
Gas Bounds
Count max storage reads per liquidation call: (reserves_in_position × oracle_reads_per_reserve × token_ops_per_reserve). Compare against chain's per-transaction resource limit (EVM: block gas; Stellar: ~40 read ledger entries; Solana: CU budget). If a user controlling N reserves can exceed the limit → document "LIQUIDATION_RESOURCE_DOS".
Grep for maximum asset/reserve count limits enforced at deposit/borrow entry. If none → users can grow positions until liquidation is physically impossible.
Reentrancy Guard Conflicts
If liquidation acquires a reentrancy lock: do any internal calls (token transfers, oracle reads) also require the same lock?
Pattern: liquidate() → nonReentrant → internal transfer → callback → another protocol function → same nonReentrant → revert
Enumerate all pausable functions and their pause groupings:
Function
Pause Group
Paused Independently?
deposit
withdraw
borrow
repay
liquidate
5b. Dangerous Asymmetries
Check for these specific asymmetric pause states:
Repay paused, liquidation active: Borrowers cannot repay but can be liquidated (interest accrues, HF drops, user has no recourse)
Borrow paused, repay active: Safe asymmetry (users can reduce risk but not increase it)
Withdraw paused, deposit active: Users can add funds but cannot exit (potential trap)
Liquidation paused, interest active: Underwater positions grow worse with no resolution
5c. Post-Unpause Grace Period
Grep for grace_period/cooldown/delay/repay_window near unpause/set_pause logic. If NONE found → document "NO_UNPAUSE_GRACE": positions that became liquidatable during pause are seized immediately upon unpause with no user recourse.
Cross-reference with ORACLE_ANALYSIS skill for general oracle checks. This section covers lending-specific oracle concerns only.
6a. Price-Token Matching
Does the oracle price correspond to the EXACT token used as collateral/debt? (not a wrapper, not an underlying)
For rebasing tokens or interest-bearing tokens: is the oracle price adjusted for the rebase/interest?
For LP tokens used as collateral: how is the LP token priced? (underlying reserves * oracle prices, or direct LP oracle?)
6b. Stale Oracle Impact on Liquidation
If the oracle returns a stale price: can liquidation proceed with outdated prices?
If a staleness check reverts: does liquidation revert too? (stale oracle = liquidation DoS)
Grep for fallback/backup/secondary/chainlink_fallback near oracle/price logic. If NONE found → document "NO_FALLBACK_ORACLE": single oracle failure = liquidation DoS for all positions using that price feed.
6c. Self-Liquidation via Oracle Manipulation
Can a borrower manipulate the oracle to inflate their collateral value, borrow maximum, then let the oracle correct?
Can a liquidator manipulate the oracle to make a healthy position appear liquidatable?
For oracle-based liquidation bonus: can the bonus be manipulated via price feeds?
Admin-controlled collateral factor changes with timelock: Retroactive effect is by design when timelock gives users notice period to adjust positions
Interest accrual paused during emergency pause IF repayment is also paused: Symmetric pause - neither debt nor repayment ability changes
Dust positions below minimum borrow size: If minimum borrow is enforced at creation AND after partial repayment, dust positions cannot be created by users
Liquidation bonus exceeding collateral for tiny positions: If minimum borrow size prevents tiny positions, the economics are safe for all valid positions
Oracle staleness check reverting liquidation: If a fallback oracle exists and is used when primary is stale, liquidation is not blocked
Step Execution Checklist (MANDATORY)
Section
Required
Completed?
Notes
1. Health Factor Boundary Analysis
YES
HF computation, boundaries, dust economics
1d. Guard Selector Enforcement
IF transaction guard pattern detected
Post-tx HF check completeness per selector
2. Interest Accrual Correctness
YES
Timing, index ordering, precision, pause interaction