
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.
src/libraries/logic/InterestLogic.solFunction: updateInterestRatesLines: 108-165src/PriceOracle.solFunction: getAssetPriceFromChainlinkLines: varioussrc/libraries/logic/GenericLogic.solFunction: calculateUserAccountDataLines: varioussrc/libraries/logic/IsolateLogic.solFunction: executeIsolateBorrowLines: variousThe analyzed contract is a complex DeFi lending and yield protocol supporting ERC-20 and ERC-721 collateral, flash loans, isolate lending with NFT auctions, cross-lending, and yield staking via a modular proxy architecture. The analysis identified 0 critical, 7 high, 12 medium, 7 low, and 4 informational findings across 33 total submissions. The single most dangerous pattern is the intentional omission of the nonReentrant guard on flashLoanERC20, which is compounded by the absence of availableLiquidity accounting during flash loan execution, creating a composable path for double-spend exploitation of pool liquidity. The protocol presents substantial risk in its current state; several high-severity logic errors affecting fund accounting, NFT auction winner attribution, and oracle staleness represent material threats to user assets and protocol solvency.
In an NFT auction, the winning bidder pays a bid amount that is held in escrow. When the auction is finalized, the NFT should go to the highest bidder. Instead, the NFT is sent to whoever calls the liquidation function, which can be any address. This means a third party can call the function after the auction ends, receive the valuable NFT for free, while the legitimate winning bidder loses both their bid funds and the NFT they won.
When a flash loan sends tokens out of the pool, the pool's internal record of how much liquidity is available is never reduced. During the flash loan, if a re-entrant call (enabled by the missing reentrancy guard on the same function) triggers a borrow or withdrawal, the pool will believe it still has the full amount of tokens and allow the operation, even though those tokens are already held by the flash loan receiver. This enables double-spending of pool liquidity.
The flash loan function deliberately skips the reentrancy lock. This means while a flash loan is active and tokens have left the pool, an attacker's contract can call back into the pool to deposit, borrow, or withdraw. Combined with the fact that available liquidity is not updated during flash loans, this creates a window where an attacker can interact with the pool as if the flash-loaned funds are still present, potentially draining funds or creating undercollateralized positions.
1 centralization point identified
Exploitation requires pool admin privileges; the threat is a malicious or compromised admin draining ETH from proxy contracts to an arbitrary address.
emergencyEtherTransfer()flashLoanERC20 deliberately omits the nonReentrant modifier, leaving the reentrancy lock unset during the entire flash loan callback. Simultaneously, erc20TransferOutOnFlashLoan does not decrement assetData.availableLiquidity. A malicious receiver contract calls back into crossBorrowERC20 or withdrawERC20 during the executeOperationERC20 callback. Because availableLiquidity still reflects the full pool balance (the flash-loaned amount was not subtracted), borrow and withdrawal validations pass, allowing the attacker to extract additional funds beyond what the pool can actually cover. The flash loan is then repaid from the originally borrowed tokens, leaving the protocol with net-negative liquidity.
Because flashLoanERC20 has no reentrancy guard, a receiver contract can call back into borrow functions while the flash loan is in-flight, temporarily driving pool utilization to near 100%. The interest rate model computes rates from instantaneous utilization. If updateInterestIndexs is triggered within this high-utilization window (e.g., via a borrow call during reentrancy), borrowers accrue interest at the manipulated peak rate for the duration of the block. This is the documented Ajna Protocol pattern, and the missing nonReentrant on flashLoanERC20 is the enabling precondition.
executeIsolateLiquidate transfers the auctioned NFT to the liquidation caller rather than the recorded lastBidder. A stale Chainlink price (no heartbeat check in getAssetPriceFromChainlink) can delay the health factor breach detection, giving an attacker time to monitor when an auction is ending. The attacker waits for the auction to conclude, calls isolateLiquidate, and receives the NFT while the legitimate bidder forfeits their bid funds. The oracle staleness issue extends the window during which this scenario is viable by slowing liquidation triggers.
| Agent | Status | Findings | Severity | Confidence | Duration | Coverage |
|---|---|---|---|---|---|---|
| reentrancy | success | 4 | 2M1L | 49% | 1.1m | Cross-function reentrancy across all module entry points (BVault, CrossLending, CrossLiquidation, IsolateLending, IsolateLiquidation, FlashLoan, Yield), Flash loan callback patterns in FlashLoanLogic - ERC20 and ERC721, ERC-721 safeTransferFrom callback patterns in VaultLogic deposit/withdraw, ERC-20 token transfer hooks (ERC-777 compatibility) in all transfer functions, nonReentrant modifier placement and coverage across all public module functions, CEI (Checks-Effects-Interactions) ordering in BorrowLogic, SupplyLogic, IsolateLogic, LiquidationLogic, YieldLogic, Cross-contract reentrancy between YieldStakingBase and pool contracts, Read-only reentrancy on view functions (getUserAccountData, getAssetSupplyData) that could be read mid-transaction, Proxy dispatch mechanism and reentrancy lock storage consistency, IsolateAuction bid refund pattern, ERC-1155 callback patterns - no ERC-1155 transfers found in core logic, YieldAccount execute/executeWithValue external call patterns |
| access control | success | 7 | 1H2M | 57% | 1.4m | Access control on all public/external functions in modules (BVault, Configurator, CrossLending, CrossLiquidation, FlashLoan, Installer, IsolateLending, IsolateLiquidation, Yield, PoolLens), Reentrancy guards - presence of nonReentrant on state-modifying functions, Flash loan reentrancy - explicit missing nonReentrant on flashLoanERC20, ERC721 safeTransferFrom callback vectors in flash loans and liquidity transfers, Initializer protection - disableInitializers in constructors of upgradeable contracts, Ownership and role management in ACLManager and AddressProvider, Proxy dispatch mechanism and delegatecall safety, YieldStakingBase repay logic - fund flow between nftOwner and botAdmin, PriceOracle staleness checks and fallthrough logic, Native ETH wrapping/unwrapping amount consistency, IsolateLiquidation bid escrow and auction state machine, Cross-chain replay (not applicable - no bridge contracts), Signature replay (no signatures used in this protocol), Storage collision (custom storage slot pattern via StorageSlot library), tx.origin authorization usage (none found) |
| economic | success | 8 | 2H2M1L | 76% | 1.6m | Chainlink oracle integration - latestRoundData validation checks in PriceOracle.getAssetPriceFromChainlink, SDAIPriceAdapter - staleness and deprecated latestAnswer() usage, Flash loan reentrancy guards - flashLoanERC20 missing nonReentrant, Interest rate model - instantaneous utilization calculation for flash loan manipulation, Liquidation logic - health factor calculations using spot oracle prices, YieldStakingBase - repay flow with botAdmin/nftOwner distinction, YieldLogic - yield cap calculation using wrong index, Flash loan ERC20/ERC721 flows in FlashLoanLogic, BVault native token withdrawal amount mismatch, ValidateLogic - asset basic validation group check, Cross-lending borrow/repay/liquidate flows, Isolate lending auction/redeem/liquidate flows, Interest index update logic and per-block guards, Supply logic deposit/withdraw ERC20 and ERC721, Proxy dispatch mechanism and trusted sender validation, MEV exposure in liquidation functions, Share math rounding in ShareUtils, Fee-on-transfer token handling in VaultLogic transfer checks, YieldAccount execute() access control, YieldRegistry createYieldAccount access control |
| logic validation | success | 10 | 1H3M2L | 78% | 2.0m | Input validation on all major function parameters (borrow, repay, deposit, withdraw, liquidate), Array length mismatch checks between parallel input arrays, Arithmetic safety in interest rate calculations (InterestLogic, MathUtils), Share price calculation patterns in YieldStakingBase (ShareUtils), Reentrancy protection coverage across all modules, Flash loan implementation and reentrancy guards, State machine integrity for loan lifecycle (ACTIVE, AUCTION, REPAID, DEFAULT), Oracle staleness checks in PriceOracle, Fee collection and treasury accounting, Isolate loan redeem/liquidate logic for correct account attribution, Group/asset cap validation (off-by-one checks), Native token wrapping/unwrapping in BVault and CrossLending, Yield staking base stake/unstake/repay flows, Access control via ACLManager for admin functions, ERC-20 flash loan liquidity accounting, Proxy dispatch mechanism and trusted sender validation, Unbounded loops over dynamic arrays in GenericLogic, YieldLogic borrow cap calculation index usage |
| code quality | success | 12 | 76% | 2.6m | Reentrancy patterns across all modules (BVault, FlashLoan, CrossLending, IsolateLending, IsolateLiquidation, Yield), Flash loan ERC20/ERC721 callback flow and reentrancy protection, Access control on admin functions (Configurator, Installer, PoolManager, ACLManager), Oracle staleness validation in PriceOracle, Interest rate calculation logic in InterestLogic and DefaultInterestRateModel, Liquidation logic in LiquidationLogic and IsolateLogic, Supply/withdraw/borrow/repay accounting in VaultLogic, Native ETH wrapping/unwrapping flow in BVault, CrossLending, IsolateLending, Yield staking logic including stake/unstake/repay with bot admin, ERC721 flash loan ownership and loan status validation, Integer overflow/underflow (Solidity 0.8 protections), Downcast safety in InterestLogic (toUint128, toUint40), Fee accrual and treasury collection logic, Proxy dispatch mechanism and trusted sender pattern, ERC20/ERC721 standard compliance for protocol interfaces, Cross-function reentrancy via proxy dispatch, Health factor calculations in GenericLogic and ValidateLogic, Bid amount escrow logic in IsolateLogic auction/redeem/liquidate, YieldStakingBase share math and account tracking | |
| compiler bugs | success | 5 | 1H1L | 76% | 1.4m | Reentrancy patterns across all modules (BVault, CrossLending, FlashLoan, IsolateLending, IsolateLiquidation), Flash loan ERC20 missing nonReentrant modifier, Access control on administrative functions (ConfigureLogic, PoolLogic, ACLManager), YieldStakingBase stake/unstake/repay flow and botAdmin re-entrancy, YieldStakingBase._repay msg.sender vs nftOwner accounting discrepancy, Native token (ETH) wrap/unwrap flows in BVault and CrossLending, Amount adjustment not propagated back to caller in withdrawERC20, IsolateLogic borrow/repay/auction/redeem/liquidate accounting, InterestLogic index update timing and reentrancy safety, LiquidationLogic ERC20 and ERC721 liquidation paths, Oracle price validation in PriceOracle and SDAIPriceAdapter, Proxy dispatch mechanism and trailing parameter unpacking, Storage slot isolation via StorageSlot library, Integer overflow/underflow in math libraries (WadRayMath, PercentageMath), Supply cap and borrow cap enforcement, Health factor checks in borrow and withdraw paths, Compiler bug patterns - pragma ^0.8.0 is outside all affected compiler bug ranges |
| assembly safety | success | 9 | 1H2M | 73% | 1.9m | All assembly blocks in Base.sol, BaseModule.sol, Proxy.sol, StorageSlot.sol, WadRayMath.sol, PercentageMath.sol, PoolManager.sol - checked for reversed shift arguments, free memory pointer corruption, extcodesize bypasses, selfdestruct paths, and unchecked delegatecall returns, Full codepoint scan for non-ASCII characters, RTLO (U+202E), zero-width joiners/spaces (U+200B/200C/200D), and Cyrillic homoglyphs in all identifiers, function names, and string literals - none found, Assembly in Proxy.sol fallback for log emission and dispatch forwarding - verified correct, Assembly in PoolManager.sol dispatch() - verified delegatecall pattern, success check present, Assembly in BaseModule.sol unpackTrailingParamMsgSender/unpackTrailingParams - calldataload at sub(calldatasize(), 40/20) - legitimate trailing parameter pattern used by Euler-style module system, StorageSlot named-slot pattern using keccak256-derived position - safe pattern, Flash loan reentrancy surface - flashLoanERC20 intentionally omits nonReentrant, Native token wrapping/unwrapping flows in BVault, CrossLending, IsolateLending modules, Yield staking bot admin multi-user flows in YieldStakingBase, Oracle staleness validation in PriceOracle, Isolate auction/redeem/liquidate accounting flows, Cross liquidation debt repayment and collateral transfer flows, Interest index update timing and ordering, Supply and borrow cap enforcement, ACL role-based access control on all admin functions, Proxy dispatch security - moduleId validation, trusted sender check, ERC721 flash loan owner check and loan status check, YieldSavingsDai protocolRequestWithdrawal stub (intentionally empty for single-step protocols) |
| l2 specific | success | 10 | 2H3M2L | 80% | 1.9m | Reentrancy guards across all module functions (BVault, CrossLending, CrossLiquidation, FlashLoan, IsolateLending, IsolateLiquidation, Yield), Flash loan implementation for reentrancy and missing guard, Cross-chain / L2 markers - none found, contract is L1-targeted based on code signals, Oracle price staleness checks in PriceOracle.sol, Isolate auction flow: bid, redeem, liquidate - NFT transfer recipients, Borrow/repay accounting for cross and isolate lending, Native token (ETH) wrap/unwrap flows in BVault and CrossLending, Yield staking flows: stake, unstake, repay in YieldStakingBase, YieldLogic cap calculations with index usage, ACLManager access control - role-based functions, ConfigureLogic pool/asset configuration logic, Interest index update timing and calculation, ERC721 token data ownership and locker address checks, Proxy dispatch mechanism and trusted sender validation, Installer module installation and proxy creation, Storage slot collision analysis for diamond/module pattern, Arithmetic overflow/underflow (Solidity 0.8.x mitigations), Supply and borrow cap enforcement, Health factor calculation in GenericLogic, Liquidation logic correctness for ERC20 and ERC721 collateral |
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/eeab86da-71c1-481e-9f06-d1a45d61e4a9)
<a href="https://walletguard.ai/audit/eeab86da-71c1-481e-9f06-d1a45d61e4a9"> <img src="https://walletguard.ai/api/badge/eeab86da-71c1-481e-9f06-d1a45d61e4a9" alt="WalletGuard Audit Badge" /> </a>