
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/ILOPool.solFunction: buyLines: 93-128src/base/Multicall.solFunction: multicallLines: 14-29src/interfaces/IILOVest.solFunction: vestingStatusLines: 28-31The analyzed contract is an Initial Liquidity Offering (ILO) platform built on Uniswap V3, encompassing pool management, whitelist enforcement, vesting, and fee distribution. The analysis identified 0 critical, 10 high, 6 medium, 7 low, and 6 informational findings across 29 total submissions prior to deduplication and gate filtering. The single most dangerous pattern is the shared Uniswap V3 position fee collection in ILOPool.claim(), where a single claimer can drain fees belonging to all other investors due to the use of type(uint128).max in pool.collect() against a shared position. Overall, this contract presents significant security risk and should not be deployed in its current form; multiple high-severity findings with clear exploit paths affect fund distribution, access control, and oracle integrity.
All investors in an ILO share a single Uniswap V3 liquidity position. When any investor calls claim(), the contract withdraws ALL accumulated trading fees for that entire shared position, not just the fees owed to that one investor. The first person to claim gets credit for fees proportional to their share, but the rest of the collected fees go to the fee collector rather than to the other investors. This means subsequent claimers receive nothing from the fee pool that had already been drained, effectively stealing trading fee income from every other participant.
Each investor is supposed to be limited to a maximum investment amount per address. However, an investor can buy up to their maximum, transfer their NFT position to a different wallet, then buy up to the maximum again with the same original address. By repeating this cycle, a single entity can accumulate far more than the intended cap, undermining the fairness of the token sale and allowing a single actor to dominate the ILO allocation.
The function that launches the ILO and deploys all raised funds into Uniswap can be called by absolutely anyone, not just the project organizer. Once the launch time passes and the Uniswap pool price happens to match the initial price, any external party can trigger the launch. Combined with the finding that the price check uses an exact equality that can be manipulated, this creates a situation where an attacker can force a project to launch at an unintended time or in unfavorable conditions.
1 centralization point identified
This is a known design-level property of the approveAndCall pattern; the risk is user-self-inflicted and requires the user to voluntarily call the function with a malicious address they control.
approveAndCall()Finding 15 (UniswapV3Oracle TWAP period degrades to near-spot on new pools) enables an attacker to manipulate the VULT/WETH pool price via a flash loan or large swap in the same block. Finding 6 (Whitelist.checkWhitelist uses unvalidated oracle) means that the manipulated oracle price is used directly to estimate the ETH value of a VULT purchase. If the attacker depresses the reported ETH price per VULT, the _maxAddressCap check passes even for purchases that should exceed the cap. The attacker can then acquire far more VULT tokens than permitted during the whitelist period, concentrating token holdings and undermining the fairness of the whitelist mechanism.
Finding 12 (ILOManager.launch() has no access control) allows any external caller to trigger launch once launchTime passes. Finding 4 (ILOManager.launch() requires exact sqrtPrice match) shows that the price check uses spot price from slot0(), not TWAP. An attacker can monitor the pool, perform swaps to bring the current pool price exactly to initialPoolPriceX96, and then immediately call launch() in the same transaction or bundle, forcing the project to launch at a moment of the attacker's choosing. This bypasses any intended timing control by the project admin and can be used to launch when conditions benefit the attacker.
Finding 1 (pool.collect with type(uint128).max drains shared fees) and Finding 18 (unchecked subtraction amountCollected0 - amount0 can underflow in Solidity 0.7.6) interact as follows: because pool.collect() is called with type(uint128).max and collects fees from ALL investors, the relationship between amountCollected and the per-user computed amount0 can diverge unpredictably across claims. In Solidity 0.7.6 without overflow protection, if rounding or the shared-fee drain causes amount0 to exceed amountCollected0 in a subsequent claim, the subtraction silently wraps to a huge number. The resulting fee transfer attempt will revert, permanently DoS-ing claim() for affected investors.
Finding 9 (tx.origin used for ownership in ILOManager constructor) establishes that ownership is set to tx.origin at deployment time, which in a factory deployment context is the EOA invoking the factory. Finding 13 (ILOManager.initialize() can be called by anyone before first initialization) shows that initialize() has no ownership check, only a whenNotInitialized guard. If the deployment transaction and initialization transaction are separate, a front-runner observing the deployment transaction can call initialize() first, setting themselves as owner and feeTaker and seizing full protocol control.
| Agent | Status | Findings | Severity | Confidence | Duration | Coverage |
|---|---|---|---|---|---|---|
| reentrancy | success | 10 | 2H | 64% | 2.0m | Classic reentrancy (CEI pattern) in ILOPool.buy(), claim(), claimRefund(), claimProjectRefund(), Cross-function reentrancy across buy/claim/refund sharing position state, ERC-777 callback reentrancy via token transfer hooks in buy() and claim(), ERC-1155 callback patterns - not present in this contract, Read-only reentrancy in view functions (vestingStatus, positions, totalSold), Whitelist.receive() self-whitelist reentrancy via ETH transfer callback, approveAndCall in Vultisig - arbitrary external call pattern, Oracle manipulation in UniswapV3Oracle.peek() and Whitelist.checkWhitelist(), Launch price validation logic in ILOManager.launch(), Access control on project admin functions, tx.origin usage in ILOManager constructor, buy() recipient != msg.sender whitelist bypass logic, Multicall payable delegation - msg.value reuse risk, uniswapV3MintCallback authentication (msg.sender == _cachedUniV3PoolAddress), ILOPool.launch() vesting configuration and liquidity distribution, refundable() modifier state transition safety, Integer overflow/underflow in fee deduction calculations, Fee taker transfer arithmetic (amountCollected - amount) potential underflow |
| access control | success | 9 | 3H2M | 78% | 1.5m | Access control on all public/external functions in ILOManager, ILOPool, Whitelist, VultisigWhitelisted, and Vultisig, Initializer protection in ILOPool (Initializable base), ILOManager, tx.origin usage for authorization in ILOManager constructor, Signature-based authentication (none found), Multicall delegatecall with payable pattern, ERC721 transfer hooks and reentrancy in claim(), Oracle manipulation risks in Whitelist.checkWhitelist(), Per-user cap bypass in ILOPool.buy() via NFT transfer, Launch function access control, Clone deployment front-running of initialize(), Fee calculation correctness in _deductFees(), Vesting schedule validation in ILOVest, Whitelist self-registration via ETH transfer, approveAndCall pattern in Vultisig, TWAP oracle reliability in UniswapV3Oracle, Ownership transfer patterns |
| economic | success | 10 | 2H | 73% | 1.8m | Flash loan attack vectors on price calculations and balance dependencies, Oracle manipulation in UniswapV3Oracle (TWAP window, spot price fallback), Whitelist ETH cap enforcement via oracle price, ILOPool buy() access control and per-user cap enforcement, ILOPool claim() fee collection and distribution logic, ILOPool launch() price verification using slot0(), ILOManager launch() permissionless execution and price manipulation, Governance attack vectors (project admin privileges), Reentrancy in Whitelist.receive() and ILOPool.claim(), Integer overflow/underflow in fee calculations, MEV exposure in launch and claim operations, Sybil resistance in whitelist mechanism, VultisigWhitelisted _beforeTokenTransfer hook, Vultisig approveAndCall() pattern, ILOVest vesting schedule validation, Multicall payable msg.value reuse risk, ChainId library usage for salt generation, PoolAddress deterministic address computation |
| logic validation | success | 13 | 2H1M3L | 72% | 2.2m | Input validation on all public/external functions in ILOPool, ILOManager, Whitelist, Vultisig, Arithmetic safety in Solidity 0.7.6 (no built-in overflow protection) — all arithmetic operations, State machine integrity for ILO lifecycle (buy, launch, refund, claim), Reentrancy vectors in claim(), buy(), claimRefund(), Oracle manipulation in UniswapV3Oracle and Whitelist.checkWhitelist(), Multicall delegatecall with msg.value pattern, NFT transfer during claim() — ownerOf called multiple times, Fee calculation underflow in claim() — amountCollected - amount, Unbounded loops in launch() and claimRefund(), Timestamp dependence in vesting and sale period checks, abi.encodePacked usage in PositionKey and ILOManager salt, EIP-712 and signature replay — not applicable, no signatures used, Access control on all owner/admin functions, Whitelist bypass vectors, Vesting schedule validation in ILOVest, Precision loss in fee and liquidity calculations using FullMath, Downcast safety in uint128 conversions |
| code quality | success | 16 | 78% | 2.4m | ERC-20 compliance (Vultisig, WETH9), ERC-721 compliance (ILOPool), Access control on all external/public functions, Integer overflow/underflow (especially Solidity 0.7.6 code), Reentrancy vectors in claim(), buy(), launch(), claimRefund(), Oracle manipulation in UniswapV3Oracle and Whitelist, State machine transitions (launch, refund, claim states), Cross-function logic in ILOManager and ILOPool, Whitelist bypass vectors, Signature/authentication in approveAndCall, Gas optimization opportunities, Proxy/initialization patterns in ILOManager and ILOPool, Unchecked arithmetic in Solidity 0.7.6 code, External call safety (TransferHelper patterns) | |
| compiler bugs | success | 6 | 1M | 75% | 1.3m | ILOPool.buy() - whitelist, cap checks, fund transfer, NFT minting logic, ILOPool.claim() - liquidity calculation, fee deduction, token transfers, ownerOf() usage, ILOPool.launch() - access control, liquidity deployment, vesting assignment, ILOPool.claimRefund() / claimProjectRefund() - refund conditions, token transfers, ILOManager.launch() - access control, price check, ILOManager.initProject() / initILOPool() - project admin controls, ILOManager.claimRefund() - refund deadline enforcement, UniswapV3Oracle.peek() - TWAP period fallback, price manipulation risk, Whitelist.checkWhitelist() - oracle-based ETH estimation, cap enforcement, VultisigWhitelisted._beforeTokenTransfer() - whitelist contract interaction, Vultisig.approveAndCall() - approve-then-call pattern, Multicall - delegatecall usage with msg.value, PeripheryPayments - WETH payment logic, ILOVest._unlockedLiquidity() - vesting schedule calculations, Compiler bug patterns for Solidity 0.7.6 (no ABIEncoderV2 storage array bug triggers found), Reentrancy vectors in claim() and buy(), Integer overflow/underflow in Solidity 0.7.6 unchecked arithmetic contexts |
| assembly safety | success | 10 | 1H1M1L | 74% | 2.0m | Full source codepoint scan for non-ASCII characters, RTLO (U+202E), zero-width joiners/spaces, and Cyrillic homoglyphs — none found, All assembly{} blocks in TickMath.sol, FullMath.sol, ChainId.sol, Multicall.sol — checked for reversed shift arguments, hardcoded slot access, return vs leave, selfdestruct patterns, ILOPool.sol buy() logic — whitelist bypass, cap enforcement, msg.sender vs recipient mismatch, ILOPool.sol claim() — CEI ordering, fee accounting, TOCTOU with ownerOf(), ILOPool.sol launch() — access control, fund flow, ILOManager.sol launch() — access control (anyone can call), ILOManager.sol initialize() — whenNotInitialized pattern, ownership, Whitelist.sol checkWhitelist() — oracle manipulation, ETH cap bypass, Whitelist.sol receive() — reentrancy via ETH refund, UniswapV3Oracle.sol peek() — TWAP manipulation with short observation periods, Vultisig.sol approveAndCall() — arbitrary external call risk, VultisigWhitelisted.sol _beforeTokenTransfer() — whitelist contract trust, ILOVest.sol _validateSharesAndVests() — share validation logic, Multicall.sol — delegatecall patterns, msg.value reuse, PeripheryPayments.sol — ETH/WETH payment logic, LiquidityManagement.sol — uniswapV3MintCallback authentication, All integer arithmetic for overflow/underflow (Solidity 0.7.6 and 0.8.24 contexts), ERC721 enumerable usage and tokenOfOwnerByIndex assumptions, Clones.cloneDeterministic salt construction for uniqueness |
| l2 specific | success | 12 | 1M3L | 71% | 2.4m | ILOPool.buy() — whitelist checks, cap enforcement, liquidity computation, token transfer ordering, ILOPool.claim() — fee deduction logic, collect() semantics, token recipient correctness, reentrancy, ILOPool.launch() — liquidity deployment, vesting NFT minting, share accounting, ILOPool.claimRefund() / claimProjectRefund() — refund mechanics, state cleanup, ILOManager.launch() — access control, price check, ILOManager.initialize() — front-running risk, access control, ILOManager constructor — tx.origin usage, Whitelist.checkWhitelist() — oracle dependency, cap enforcement, Whitelist.receive() — self-whitelist via ETH, reentrancy via .transfer(), UniswapV3Oracle.peek() — TWAP period, formula correctness, OracleLibrary — observation period edge cases, VultisigWhitelisted._beforeTokenTransfer() — whitelist contract interaction, Vultisig.approveAndCall() — ERC20 approve+call pattern, ILOVest._validateSharesAndVests() — vesting schedule validation, ILOWhitelist — whitelist state management, Multicall — delegatecall with payable msg.value risks, LiquidityManagement.uniswapV3MintCallback() — callback validation, Cross-function reentrancy paths in ILOPool, Integer overflow/underflow in fee calculations, L2 markers scan — no L2-specific imports or predeploy addresses found (Chain ID 1 = mainnet) |
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/77adeb68-2186-4196-8438-45fb57e391d7)
<a href="https://walletguard.ai/audit/77adeb68-2186-4196-8438-45fb57e391d7"> <img src="https://walletguard.ai/api/badge/77adeb68-2186-4196-8438-45fb57e391d7" alt="WalletGuard Audit Badge" /> </a>