
Loading...
Loading
Loading...
LoadingLoading audit report...

WalletGuard.ai, powered by Gestalt Labs
Findings selected for deep verification. Where possible we generated a Solidity proof-of-concept and executed it against a forked mainnet.
NaiveReceiverPool.solFunction: flashLoan, withdrawLines: 47, 68The analyzed contract is a WETH-backed flash loan pool implementing ERC-3156, with ERC-2771 meta-transaction support via a trusted forwarder and Multicall batching inherited from a Multicall utility contract. The analysis identified 2 critical, 1 high, 2 medium, 0 low, and 1 informational finding. The single most dangerous pattern is the combination of an unrestricted ERC-2771 trusted forwarder with Multicall delegatecall batching, which allows an attacker to forge any depositor's identity and drain all pool funds in a single atomic transaction without holding any flash loan capital. The contract is unsafe for deployment in its current form; any depositor funds and the FlashLoanReceiver balance are fully extractable by an unprivileged attacker.
The function that lets users withdraw their deposited ETH identifies the caller using a mechanism that can be forged. Any attacker can disguise themselves as any other depositor and withdraw that depositor's entire balance to the attacker's own address, with no special permissions required.
Anyone can trigger a flash loan against the FlashLoanReceiver contract, which automatically pays a fixed fee of 1 ETH per call. By calling this 10 times, an attacker drains all 10 ETH from the receiver into the pool's fee account, then withdraws it. No capital is required from the attacker.
The contract inherits a batch-call feature that lets an attacker combine the fee-draining attack and the identity-forgery attack into a single unstoppable transaction, making it impossible for anyone to intervene between steps. All funds in both the receiver contract and the depositor pool can be stolen atomically.
No specific centralization concerns identified.
An attacker constructs a single multicall transaction routed through the trustedForwarder. Each sub-call executes under msg.sender == trustedForwarder, so _msgSender() reads the last 20 bytes of msg.data as the effective sender. Step 1: the attacker batches 10 calls to flashLoan(FlashLoanReceiver, weth, 0, ''), each charging FlashLoanReceiver exactly 1e18 WETH in fees that are credited to deposits[feeReceiver]. Step 2: the attacker appends the feeReceiver address as the last 20 bytes of the multicall calldata, then calls withdraw(10e18, attackerAddress), causing _msgSender() to return feeReceiver and debiting deposits[feeReceiver] to transfer 10e18 WETH to the attacker. Steps 1 and 2 execute atomically. The zero-amount flash loan validation gap (finding 7) enables step 1 to work with amount=0, requiring no attacker capital. The totalDeposits accounting inconsistency (finding 5) means pool depositors also face an inflated totalDeposits relative to actual WETH holdings after this attack, potentially blocking their own withdrawals. The entire operation costs only gas, requires no deposits from the attacker, and is irreversible.
Without relying on flash loans, an attacker who can call the pool through or as the trustedForwarder appends a victim's Ethereum address as the last 20 bytes of any call to withdraw(). The _msgSender() override returns this appended address, debiting deposits[victim] and transferring WETH to the attacker-controlled receiver parameter. Because receiver is unconstrained, the attacker redirects the victim's funds to themselves. This attack path targets every depositor independently of the FlashLoanReceiver and requires only knowledge of the trustedForwarder's call path.
| Agent | Status | Findings | Severity | Confidence | Duration | Coverage |
|---|---|---|---|---|---|---|
| access control | success | 3 | 1C | 95% | 47.6s | Access control on all external functions (flashLoan, withdraw, deposit), _msgSender() EIP-2771 meta-transaction pattern and its interaction with Multicall, Multicall + trustedForwarder calldata spoofing attack vector, Flash loan accounting (totalDeposits, deposits mapping), Flash loan receiver griefing: anyone can flash-loan against FlashLoanReceiver, Signature replay patterns (no signatures present in this contract), Reentrancy during flashLoan callback, Integer overflow/underflow (Solidity 0.8.25, protected), WETH transfer/transferFrom return value handling (WETH always reverts on failure) |
| assembly safety | success | 3 | 1H | 89% | 48.5s | Full source codepoint scan for non-ASCII / invisible characters (none found), Assembly block presence (none present in this file), _msgSender() trusted forwarder override and calldata tail extraction, Multicall inheritance and interaction with _msgSender, flashLoan() accounting order and reentrancy surface, withdraw() access control via _msgSender, deposit() / _deposit() accounting correctness, ERC3156 compliance (CALLBACK_SUCCESS check, fee handling), Integer overflow/underflow in Solidity 0.8.25, Flash loan fee extraction attack via zero-amount loans, feeReceiver deposit accumulation and withdrawal path |
| cipher alpha | success | 4 | 84% | 14.2m | - | |
| cipher beta | success | 8 | 1C | 89% | 12.6m | - |
| cipher general | success | 15 | 2M | 88% | 16.6m | - |
| code quality | success | 4 | 78% | 47.6s | ERC-3156 flash loan compliance, trustedForwarder meta-transaction _msgSender() bypass, Multicall interaction with _msgSender(), Deposit/withdrawal accounting, Fee accumulation and feeReceiver drain path, WETH transfer return value checks, Access control on flashLoan initiator, Integer overflow/underflow in deposit accounting, Reentrancy via flashLoan callback | |
| compiler bugs | success | 2 | 93% | 36.4s | Pragma version 0.8.25 - no relevant compiler bugs in this version range, ERC3156 flash loan implementation - fee charging logic, amount=0 attack vector, trustedForwarder meta-transaction spoofing via _msgSender(), Multicall batching enabling multi-call exploits in single transaction, withdraw() access control via _msgSender(), deposit() and _deposit() accounting correctness, totalDeposits accounting update ordering in flashLoan(), WETH transfer return value handling, FlashLoanReceiver auto-repay behavior enabling fee drain | |
| economic | success | 3 | 1C | 91% | 42.5s | Flash loan fee accounting and fee accumulation in deposits[feeReceiver], Access control on flashLoan() - anyone can call with any receiver, ERC-2771 meta-transaction _msgSender() override - forged sender via trustedForwarder, Multicall interaction enabling batch flash loan calls in single transaction, withdraw() using _msgSender() which is spoofable via trustedForwarder, deposit() and _deposit() crediting to _msgSender() (spoofable), totalDeposits accounting consistency across deposit/withdraw/flashLoan, WETH balance vs totalDeposits invariant, Reentrancy in flashLoan() - weth.transfer before state update (totalDeposits -= amount) |
| l2 specific | success | 3 | 93% | 47.2s | Multicall + EIP-2771 trustedForwarder interaction for msg.sender spoofing, _msgSender() override and msg.data extraction logic, flashLoan() accounting: totalDeposits increment/decrement ordering, flashLoan() fee extraction against FlashLoanReceiver, withdraw() access control via _msgSender(), deposit() and _deposit() for double-counting issues, Zero-amount flash loan attack vector, Integer overflow/underflow in Solidity 0.8.25, WETH transfer vs transferFrom authorization, feeReceiver deposit inflation over time | |
| logic validation | success | 4 | 1C | 88% | 49.9s | Flash loan logic and fee accounting in flashLoan(), Access control on flashLoan() - no initiator restriction, _msgSender() override and trustedForwarder spoofing, Multicall interaction with _msgSender(), withdraw() accounting and totalDeposits invariant, deposit() and _deposit() internal accounting, ERC3156 compliance (maxFlashLoan, flashFee), WETH balance vs totalDeposits consistency, Integer overflow/underflow in deposit/withdraw arithmetic, Constructor parameter validation |
| math verification | success | 3 | 87% | 1.3m | INVENTORY 1 - All functions: flashLoan [WRITES-MATH, EXTERNAL-MATH], withdraw [WRITES-MATH], deposit [WRITES-MATH], _deposit [WRITES-MATH], _msgSender [READS-MATH], maxFlashLoan [READS-MATH], flashFee [PURE-MATH], constructor [WRITES-MATH], INVENTORY 2 - Constants: FIXED_FEE=1e18 (line 12), CALLBACK_SUCCESS=keccak256(...) (line 13); State vars: deposits mapping, totalDeposits, INVENTORY 3 - Tokens: WETH (solmate), decimals=18; no cross-decimal arithmetic detected, INVENTORY 4 - External calls: weth.transfer(), weth.transferFrom(), weth.deposit(), receiver.onFlashLoan(), INVENTORY 5 - Cross-function pairs: (flashLoan, withdraw) share totalDeposits; (flashLoan, _deposit) share deposits mapping; multicall enables batching of any pair, A1 Fee math: FIXED_FEE=1e18 flat fee, no percentage math, checked for asymmetry, A2 Rounding: no division present, not applicable, A3 Division before multiplication: no division present, A4 Share/asset conversion: no vault pattern, A5 Liquidation incentive: not present, A6 Interest accrual: not present, A7 Reward accrual: not present, A8 AMM invariants: not present, A14 Cross-asset decimal conversion: WETH only (18 dec), no cross-decimal math, A15 Integer overflow: Solidity 0.8.25, unchecked blocks not present, A18 Cross-function math consistency: multicall+_msgSender spoofing analyzed, ERC3156 flash loan fee charging and repayment flow, trustedForwarder _msgSender() identity spoofing via calldata suffix, Multicall batching enabling repeated fee extraction against FlashLoanReceiver, Reentrancy surface during flashLoan callback with totalDeposits mid-state inconsistency | |
| reentrancy | success | 3 | 1H | 92% | 49.0s | Cross-function reentrancy via flashLoan callback, ERC-3156 flash loan implementation correctness, Multicall batching attack surface, ERC-2771 _msgSender() spoofing via trustedForwarder, CEI ordering in flashLoan(), Deposit/withdraw accounting consistency, Access control on withdraw(), totalDeposits manipulation, WETH transfer and transferFrom error handling, Fee receiver accounting |
This automated audit has inherent limitations. The following areas are not covered.
This report is an automated point-in-time assessment and does not guarantee protection against all possible attacks. It does not cover off-chain components, economic modeling, or business logic correctness unless explicitly noted. Changes to the contract after the audit commit are not reviewed. This is not financial or legal advice. WalletGuard, powered by Gestalt Labs, provides this analysis as-is with no warranty of completeness.
[](https://walletguard.ai/audit/d5d29764-cddb-4edd-a05c-ca76b3d0562f)
<a href="https://walletguard.ai/audit/d5d29764-cddb-4edd-a05c-ca76b3d0562f"> <img src="https://walletguard.ai/api/badge/d5d29764-cddb-4edd-a05c-ca76b3d0562f" alt="WalletGuard Audit Badge" /> </a>