Skip to content
Last updated: February 06, 2026

SCWE-101: Flash-Loan-Fueled Governance Manipulation

Stable Version v1.0

This content is in the version-(v1.0) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Relationships

Description

If voting power is measured at execution time without historical snapshots or locking, an attacker can borrow a large balance via flash loan, pass a proposal, and return the loan in the same transaction. Governance decisions then depend on transient liquidity rather than long-term stake.

Remediation

  • Use token balance snapshots at a fixed block for voting power.
  • Require vote escrow/locking or minimum voting period spanning multiple blocks.
  • Set quorum and proposal thresholds based on historical supply, not instantaneous balances.

Examples

Vulnerable

pragma solidity ^0.8.0;

contract SimpleGov {
    IERC20 public token;
    mapping(uint256 => bool) public proposals;

    function vote(uint256 id, bool support) external {
        require(token.balanceOf(msg.sender) > 1_000_000 ether, "low power");
        if (support) { proposals[id] = true; }
    }
}

Fixed

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol";

contract SnapshotGov {
    ERC20Snapshot public token;
    mapping(uint256 => uint256) public snapshotId;

    function openProposal(uint256 id) external {
        snapshotId[id] = token.snapshot();
    }

    function vote(uint256 id, bool support) external {
        uint256 power = token.balanceOfAt(msg.sender, snapshotId[id]);
        require(power > 1_000_000 ether, "low power");
        // record vote...
    }
}