skills/vulnerability-patterns/flash-loan-attacks/SKILL.md
Flash-loan attack mechanics, exploit archetypes, and mitigations for capital-amplified threats.
npx skillsauth add apegurus/solidity-argus flash-loan-attacksInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Flash loans enable borrowing massive amounts with zero collateral, repaid within the same transaction. Attackers use this capital to:
Key insight: Any assumption about "no one would have $100M" is broken by flash loans.
1. Flash borrow $100M
2. Swap to move AMM price
3. Interact with victim (borrow, liquidate, swap)
4. Swap back
5. Repay flash loan
6. Profit
1. Flash borrow governance tokens
2. Create proposal or vote
3. If snapshot-based: exploit timing
4. Return tokens
// VULNERABLE: Instant voting power
function vote(uint256 proposalId, bool support) external {
uint256 votes = token.balanceOf(msg.sender); // Current balance!
proposals[proposalId].votes += votes;
}
// SECURE: Historical voting power
function vote(uint256 proposalId, bool support) external {
uint256 votes = token.getPastVotes(msg.sender, proposals[proposalId].snapshot);
proposals[proposalId].votes += votes;
}
Flash loans amplify reentrancy by providing initial capital:
1. Flash loan collateral asset
2. Deposit as collateral in lending protocol
3. Borrow maximum against it
4. Manipulate collateral price down
5. Abandon position (bad debt)
What happened:
Root cause: donateToReserves() increased liabilities without proper health check
Lesson: All state-changing functions must maintain invariants.
What happened:
Root cause: Oracle manipulation + flash loan capital
What happened:
Root cause: No time delay between proposal and execution
Lesson: Governance needs timelocks AND snapshot-based voting.
What happened:
Root cause: Flash-loan-vulnerable price calculation
Attackers can source flash loans from:
Combined capital: $10B+ available in single transaction
// ERC20Votes pattern
mapping(address => Checkpoint[]) private _checkpoints;
function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) {
require(blockNumber < block.number, "Block not yet mined");
return _checkpointsLookup(_checkpoints[account], blockNumber);
}
uint256 public constant TIMELOCK = 2 days;
mapping(bytes32 => uint256) public pendingActions;
function queueAction(bytes32 actionHash) external onlyGovernance {
pendingActions[actionHash] = block.timestamp + TIMELOCK;
}
function executeAction(bytes32 actionHash) external {
require(pendingActions[actionHash] != 0, "Not queued");
require(block.timestamp >= pendingActions[actionHash], "Timelock");
delete pendingActions[actionHash];
// Execute
}
// Detect if called within flash loan context
modifier noFlashLoan() {
require(
block.number > lastDepositBlock[msg.sender],
"Same block"
);
_;
}
testing
Specialist profile for mechanically applying the attack-vector deck and classifying vectors as skip, drop, or investigate.
tools
Specialist profile for libraries, helpers, base contracts, adapters, encoders, wrappers, and integration glue.
testing
Specialist profile for rounding, scale, decimal, downcast, and arithmetic accounting edge cases.
testing
Specialist profile for extracting conservation laws and state couplings, then searching for violating paths.