All audits
FailedSolanaIndependent

Solend Governance (2022)

Smart contract security audit by TierZero · 2023-12-15

Independent research. This is an unsolicited security analysis published by TierZero — not a commissioned audit, and no certificate is issued. It reflects our own review of public code and on-chain events.

Findings summary

1
Critical
2
High
2
Medium
1
Low
1
Informational

What the protocol did

Solend is a lending market on Solana, forked conceptually from Compound/Aave but built on SPL tokens and Switchboard/Pyth oracles instead of an EVM stack. Users deposit collateral (SOL, mSOL, stablecoins, LP tokens) into isolated and main-pool reserves and borrow against it; a keeper network liquidates undercollateralized obligations on-chain when health factor drops below 1. Governance runs through a SLND token and an SPL-Governance ("Realms") instance that can pass proposals executing arbitrary program instructions, including changes to reserve parameters and, as this incident showed, extraordinary emergency actions.

In June 2022, one wallet held a SOL/USDC position large enough that a normal liquidation — dumping SOL through on-chain AMMs and triggering keeper bots — risked moving the SOL price sharply and, given Solana's congestion history, degrading network liveness during the unwind. The position was a meaningful fraction of the protocol's total borrowed value in that market, which is the setup for the governance failure described below.

The vulnerability

The bug class here is not a memory-safety or arithmetic error in the lending logic — it's a governance design flaw: an emergency-powers proposal with no timelock, a quorum measured against votes cast rather than circulating supply, and a scope broad enough to let the executor take unilateral custody actions over a specific user's account. This is the DAO-governance analogue of an unscoped admin backdoor.

// Simplified SPL-Governance realm config, illustrative of the pattern
struct RealmConfig {
    min_vote_threshold_percentage: u8, // e.g. 1% — measured against
                                        // votes CAST, not total supply
    max_voting_time: u64,              // ~6 hours
    hold_timelock_after_pass: u64,     // 0 — executes immediately
}

// The proposal itself granted a scope this broad:
fn emergency_take_over_obligation(
    authority: Signer,       // Solend Labs multisig
    obligation: AccountInfo, // ANY user's obligation, no allow-list
) -> ProgramResult {
    // reassigns liquidation authority / withdraws collateral
    // outside the normal keeper/liquidation-engine path
}

Two things compound each other: SLND distribution was concentrated in a handful of wallets (team, early investors, market makers), and the quorum bar was cheap to clear with only a few large voters inside a short window. Pair that with zero timelock between "proposal passes" and "instruction executes," and you get a mechanism where a small coalition can authorize custody actions over any user's funds with no cooling-off period for the broader token holder base to react.

How the exploit worked

  1. A single account's SOL-collateralized borrow reached a size where a standard liquidation was judged likely to cause outsized price impact and network strain.
  2. Solend Labs drafted proposal SLND1, asking governance to grant the team temporary emergency authority to take over and liquidate that specific account OTC, bypassing the normal on-chain liquidation path.
  3. The proposal passed within hours. Quorum was met almost entirely by a small number of large holders — turnout as a share of total supply was low, but the threshold was denominated against votes cast, so concentration alone was sufficient.
  4. Because there was no execution timelock, the proposal was immediately actionable — effectively authorizing the team to seize a specific user's collateral without that user's consent, something no lending protocol's terms had previously implied was possible.
  5. Community backlash was immediate: this set a precedent that a small voter coalition could authorize fund seizure from any account, at will, framed as an "emergency." A follow-up proposal, SLND2, reversed SLND1 within about a day.
  6. Solend then shipped SLND3, replacing discretionary emergency powers with mechanical risk controls — per-account and per-market borrow caps that prevent any single position from growing large enough to threaten the protocol again.

No funds were technically drained by an external attacker — the "exploit" here is that the governance layer itself became the attack surface, and the fix required product changes, not just a patched instruction.

How an audit catches this

Governance modules get far less scrutiny than the lending math, and that's backwards for protocols that give governance custody-adjacent powers. Concretely, we check:

  • Quorum denominated against circulating/total supply, not votes cast, and model what a top-5-holder coalition can pass unilaterally.
  • Mandatory timelock between proposal success and execution, sized so affected users can react (withdraw, publicize, escalate) before funds move.
  • Scope-limited emergency instructions — an allow-list or explicit user consent path, never an unconstrained "authority over any account" instruction.
  • Simulated governance attack: given the actual token distribution, what's the minimum number of wallets needed to pass an arbitrary instruction inside the shortest allowed voting window?
  • Separation of powers between the entity that can propose emergency actions and the entity that approves them — no single multisig should hold both.

This is the same category of review we run for upgrade authorities on Solana programs — see our breakdown of the risks in upgradeable Solana programs and BPF loader authority — since a governance takeover and an upgrade-authority compromise land in the same place: someone other than the user gets unilateral control of their funds. It's also one of the checks we run through on every engagement, detailed in our Solana program security audit checklist.

Remediation

Solend's actual fix (SLND3) addressed the root cause rather than patching governance: hard per-account and per-market borrow caps mean no single position can grow large enough to force an "emergency" decision in the first place. That's the right order of operations — fix the risk parameters so discretionary power is never needed, then, separately, fix the discretionary power itself:

  • Minimum timelock (24–72h) on any proposal that can move or reassign user funds, no exceptions for "emergency."
  • Quorum floors set against total supply, with concentration limits or quadratic weighting to blunt whale coalitions.
  • Emergency powers scoped to parameter changes (pause, cap, oracle fallback) — never to unilateral custody of a specific account.
  • Position-size caps and isolated markets for volatile or thin-liquidity collateral, so tail risk is capped mechanically instead of requiring after-the-fact governance intervention.

If your protocol has an admin key, a multisig, or a governance module that can touch user funds, that surface deserves the same line-by-line scrutiny as your instruction handlers — it's exactly the kind of review we cover under smart contract audit.

Need your contracts audited?

Manual review + tooling across TON, Solana and EVM — certificate and full report included.

Get an audit