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

WalletGuard.ai, powered by Gestalt Labs
Forge fork-validation ran but no findings met the threshold for PoC inclusion. See the per-finding "Forge validated" badges in the report below for individual results.
The analyzed contract is a prelaunch points system that accepts ETH and ERC-20 token deposits, converts them to lpETH upon protocol activation, and distributes lpETH claims to depositors. The analysis identified 0 critical, 4 high, 12 medium, 5 low, and 2 informational findings across 26 total submissions. The single most dangerous pattern is the use of address(this).balance as the claimed ETH amount in _claim(), which allows any ETH present in the contract prior to a non-ETH token swap to be credited to the claimer, enabling theft of ETH belonging to other users. Overall, the contract contains several high-severity logic errors and reentrancy-adjacent patterns that present material risk to user funds and should be remediated before any significant value is committed to the protocol.
When a user claims a non-ETH token deposit, the contract converts their tokens to ETH and then checks the contract's total ETH balance to determine how much lpETH to mint. If an attacker sends ETH directly to the contract beforehand, that extra ETH is included in the calculation, so the attacker receives lpETH worth far more than their actual swap produced. This can be used to drain lpETH intended for other users.
The claim function accepts a percentage value between 0 and 255 with no validation. Passing 0% causes the contract to execute a swap with zero tokens and then mint lpETH for any ETH that happens to be sitting in the contract at that moment. Combined with the address(this).balance vulnerability, an attacker can use this to steal the contract's entire ETH balance by calling claim with _percentage=0 after pre-funding the contract.
When a user withdraws ETH, the contract sends the ETH before updating the total supply counter. During the brief window between receiving ETH and the counter updating, a malicious contract can call back into the claim function. Because the total supply appears larger than it should, the attacker's lpETH share calculation is inflated, potentially resulting in more lpETH than deserved.
5 centralization points identified
Exploitation requires the owner to make an erroneous call; there is no attacker-triggered path. This is a property about the privileged role and ownership mechanism that the buyer should know.
setOwner()The ability to update lpETH and lpETHVault before activation is a privileged-role property; the mechanism behavior depends on owner action and is a property the buyer should understand.
setLoopAddresses()This describes a limitation of the admin recovery mechanism; it is a property the buyer should know about regarding stuck-fund scenarios.
recoverERC20()Exploitation requires the owner to supply a zero or malicious address; this is an administrative property and configuration risk rather than an attacker-triggered vulnerability.
setLoopAddresses()This is an administrative key management concern; no attacker can trigger it without already controlling the owner account.
setOwner()An attacker combines the unrestricted _percentage parameter (Finding: Unrestricted _percentage parameter) with the address(this).balance accounting flaw (Finding: ETH balance-based claim calculation inflated by direct ETH deposits). Step 1: Attacker sends ETH directly to the contract via the receive() fallback, inflating address(this).balance above what was legitimately locked. Step 2: Attacker calls claim() with _percentage=0, producing userClaim=0 and swap data specifying 0 input tokens. Step 3: _fillQuote is called with 0 amount; no tokens are swapped. Step 4: claimedAmount = address(this).balance captures all ETH in the contract including the pre-seeded ETH. Step 5: lpETH is minted to the attacker for the full contract ETH balance, stealing ETH that belongs to other legitimate depositors.
The withdraw() function decrements totalSupply after sending ETH, creating a window where totalSupply is inflated. If a concurrent claim for a non-ETH token is executable (post-startClaimDate), an attacker's malicious receive() callback can invoke claim() while totalSupply remains elevated. The lpETH share formula claimedAmount = userStake.mulDiv(totalLpETH, totalSupply) produces a smaller denominator illusion, but because totalSupply is HIGHER than it should be during the callback the share is actually slightly lower. The more concrete risk is that the stale state enables other state-dependent functions to operate on inconsistent accounting, compounding with the address(this).balance inflation finding.
If the owner account is compromised, an attacker can call setLoopAddresses() pointing lpETH to a malicious contract. On the next call to convertAllETH(), all ETH held by the contract (the full prelaunch deposit pool) is sent via lpETH.deposit{value: totalBalance} to the attacker-controlled address. Because setLoopAddresses has no zero-address validation and no timelock, and ownership transfer is immediate with no two-step confirmation, a single compromised key enables complete fund drain with no recovery path.
| Agent | Status | Findings | Severity | Confidence | Duration | Coverage |
|---|---|---|---|---|---|---|
| reentrancy | success | 5 | 1H1M1L | 80% | 1.1m | Cross-function reentrancy in withdraw() and claim()/claimAndStake(), ETH transfer patterns and state update ordering (CEI compliance), ERC-777 callback reentrancy potential via lpETH.deposit(), Balance accounting logic for ETH, WETH, and LRT tokens, address(this).balance usage in _claim() for non-ETH tokens, approve() usage in _fillQuote() and SafeERC20 consistency, Access control on owner-only functions, Time-lock and date modifier logic, Emergency mode withdrawal paths, AttackContract mock analysis for intended exploit vectors, Swap data validation in _validateData(), totalSupply accounting across deposit/withdraw/claim flows |
| access control | success | 8 | 1M | 74% | 1.6m | Reentrancy patterns in withdraw() and _claim(), Access control on all external functions, Ownership transfer safety, Signature/authentication patterns, Emergency mode logic and bypass conditions, Token swap data validation in _validateData(), Assembly-based calldata decoding in _decodeUniswapV3Data and _decodeTransformERC20Data, ETH balance tracking in _claim() for non-ETH tokens, setLoopAddresses single-call enforcement, lpETH approval and staking in claimAndStake(), ERC20 approve patterns in _fillQuote(), Integer overflow/underflow (Solidity 0.8.20 built-in protection), Lock functions and totalSupply accounting, recoverERC20 protection against draining allowed tokens, Cross-function reentrancy between withdraw and claim |
| economic | success | 8 | 1H2M1L | 75% | 1.8m | Flash loan attack vectors on balance-based pricing in _claim(), Reentrancy in withdraw() and _claim() via ETH transfer and ERC20 safeTransfer callbacks, Oracle manipulation - no price feeds present, not applicable, Governance attacks - no governance token or voting mechanism present, Sandwich/MEV exposure in _fillQuote() swap execution, Slippage protection on token-to-ETH swaps in _fillQuote(), ETH balance manipulation in _claim() non-ETH token path, Integer overflow/underflow in share math and percentage calculations, Access control on owner-only functions, ERC20 approval patterns and USDT compatibility in _fillQuote(), Calldata decoding assembly in _decodeUniswapV3Data and _decodeTransformERC20Data, State machine transitions (loopActivation, startClaimDate, emergencyMode), recoverERC20 token protection logic, Timestamp casting safety (uint32 truncation), CEI order in withdraw() and _claim(), First depositor inflation attacks - not applicable (no share-based vault here), Fee-on-transfer token compatibility with safeTransferFrom, Harvest sandwiching - convertAllETH() is owner-only, partially mitigated, Rounding direction in mulDiv for ETH claim calculation |
| logic validation | success | 8 | 2M2L | 81% | 1.2m | Input validation on all public/external functions, Arithmetic safety including unchecked blocks and percentage calculations, Reentrancy patterns in withdraw() and _claim(), State machine integrity (loopActivation, startClaimDate, emergencyMode transitions), Timestamp dependence in modifiers and withdraw logic, ETH balance accounting in _claim and convertAllETH, External call safety in _fillQuote (approve and exchangeProxy call), ABI encoding/decoding in _decodeUniswapV3Data and _decodeTransformERC20Data, Access control on admin functions, ERC20 token compatibility (non-standard tokens), Zero-address checks on critical address setters, Denial of service vectors in loops and external calls, Percentage bounds validation in _claim |
| code quality | success | 9 | 81% | 1.4m | Reentrancy patterns in withdraw() and _claim(), ETH balance accounting in _claim() vs _fillQuote(), State mutation ordering (checks-effects-interactions pattern), Percentage arithmetic bounds and truncation, Token allowlist and recovery logic, Access control on privileged functions, Swap data validation in _validateData(), Assembly calldata decoding in _decodeUniswapV3Data() and _decodeTransformERC20Data(), ERC-20 compliance of PrelaunchPoints contract, Time-lock logic in convertAllETH() and withdraw(), SafeERC20 usage consistency, Emergency mode logic | |
| compiler bugs | success | 4 | 1H1M | 88% | 51.3s | Reentrancy patterns in withdraw() and _claim(), CEI (Checks-Effects-Interactions) ordering in all state-mutating functions, address(this).balance usage in _claim() for non-ETH token paths, _percentage bounds validation in claim(), Emergency mode interaction with startClaimDate in withdraw(), Balance zeroing before revert checks in withdraw(), ERC20 approve() usage in _fillQuote(), Access control on owner-only functions, Assembly data decoding correctness in _decodeUniswapV3Data and _decodeTransformERC20Data, Integer overflow/underflow with Solidity 0.8.20, totalSupply and totalLpETH accounting consistency, Compiler bug patterns for solc 0.8.20 (no affected bugs apply), setLoopAddresses one-time set enforcement, recoverERC20 protection against draining locked tokens |
| assembly safety | success | 10 | 1H6M | 80% | 1.7m | Full source code scanned codepoint-by-codepoint for non-ASCII characters — none found, All assembly{} blocks in _decodeUniswapV3Data and _decodeTransformERC20Data analyzed for shift direction, offset correctness, and calldataload bounds, Reentrancy in withdraw() and _claim() including ETH send and ERC20 safeTransfer paths, ETH balance accounting in _claim() for non-ETH token path (address(this).balance inflation), CEI pattern compliance in withdraw() and _claim(), Access control on all onlyAuthorized functions, Approval patterns in _fillQuote and claimAndStake, setLoopAddresses trust assumptions and impact of owner compromise, recoverERC20 exclusion list correctness, Assembly calldataload offset arithmetic in both decode functions, Modifier logic: onlyBeforeDate, onlyAfterDate, onlyAuthorized, totalSupply and totalLpETH accounting correctness, Emergency mode bypass paths, Lock/claim/withdraw state machine transitions, Integer overflow/underflow under Solidity 0.8.20 (built-in checks), WETH unwrap path in _processLock |
| l2 specific | success | 9 | 2M1L | 74% | 1.4m | Reentrancy in withdraw() and _claim() - ETH send after state changes, ETH balance accounting in _claim for non-ETH tokens (address(this).balance inflation), Integer overflow/underflow in percentage calculations, Access control on owner-only functions, setLoopAddresses single-use enforcement, Emergency mode interactions with time-based checks, Approve() non-standard ERC20 compatibility in _fillQuote, Assembly-based calldata decoding in _decodeUniswapV3Data and _decodeTransformERC20Data, totalSupply accounting consistency, recoverERC20 restrictions vs. stuck funds, Timestamp dependence for timelock and activation, L2 chain compatibility signals (none found - deployed on mainnet chain ID 1) |
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/7d5ab2e8-5572-46e2-8c41-cf5465032cd7)
<a href="https://walletguard.ai/audit/7d5ab2e8-5572-46e2-8c41-cf5465032cd7"> <img src="https://walletguard.ai/api/badge/7d5ab2e8-5572-46e2-8c41-cf5465032cd7" alt="WalletGuard Audit Badge" /> </a>