
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/pumps/MultiFlowPump.solFunction: _getSlotForAddressLines: 280-283The analyzed contract is a Basin AMM Well implementation (WellUpgradeable), a liquidity pool system supporting swaps and liquidity provision with a pluggable well function architecture, UUPS upgradeability, and an on-chain oracle pump (MultiFlowPump). The analysis identified 0 critical, 2 high, 6 medium, 7 low, and 3 informational findings across 22 total inputs after deduplication and scope review. The single most dangerous pattern is the missing ownership check in the UUPS upgrade authorization path: any caller can upgrade a Well proxy to any Aquifer-registered implementation, enabling an attacker to replace pool logic and drain all liquidity. The contract carries significant risk in its current form and should not be deployed to production until the upgrade authorization and Stable2 math bugs are remediated.
Any wallet or contract can replace the logic of a Well pool with a different (potentially malicious) implementation, as long as that implementation was previously registered in the Aquifer. Because registering a new implementation in the Aquifer is permissionless, an attacker can deploy their own malicious Well logic, register it, and then upgrade every affected pool to it, draining all deposited funds.
When a Stable2 pool is configured with a second token whose decimal count is set to zero, the contract silently fails to correct that value to 18. The pool then applies a scaling factor of 10^18 to that token's reserves, making all price calculations wildly wrong. An attacker who creates or interacts with such a misconfigured pool can extract funds by exploiting the distorted pricing.
The two-step setup process for upgradeable Wells leaves the pool without an owner between the first and second initialization calls. If the second call (init) is never made, the pool has no owner, which means ownership-based protections and upgrade controls are permanently unavailable. This creates a window where the pool is unprotected.
1 centralization point identified
No exploit path exists; this affects off-chain tooling interoperability only. Retained as an informational property buyers should be aware of.
Finding 14 (upgradeTo/upgradeToAndCall lack onlyOwner) and Finding 7 (_authorizeUpgrade missing ownership check) together confirm that any caller can trigger a UUPS upgrade on any WellUpgradeable proxy. Combined with Finding 17 (_authorizeUpgrade checks whether the new address is a Well bored by the Aquifer rather than a valid implementation contract), an attacker can bore a new malicious Well via the permissionless Aquifer, satisfying the registry check, and then call upgradeTo on a target pool to replace its logic. The malicious implementation can then drain all reserves held by the proxy.
Finding 1 (decodeWellData copy-paste bug allowing decimal1=0) creates a pool state where all Stable2 math operates on misscaled reserves. Combined with Finding 11 (getBandC intermediate division causing precision loss) and Finding 8 (calcReserveAtRatioSwap silent return of 0 on non-convergence), an attacker operating on a misconfigured or edge-case Stable2 pool can trigger cascading math failures: misscaled reserves cause Newton-Raphson non-convergence, which silently returns 0, and precision loss in getBandC compounds the error. The result is incorrect swap outputs and LP token amounts that an attacker can exploit to extract value.
| Agent | Status | Findings | Severity | Confidence | Duration | Coverage |
|---|---|---|---|---|---|---|
| reentrancy | success | 4 | 2L | 79% | 49.2s | Cross-function reentrancy in Well.sol (swapFrom, swapTo, addLiquidity, removeLiquidity, removeLiquidityImbalanced, sync, skim, shift), CEI pattern compliance in all state-modifying Well functions, ReentrancyGuard and readOnlyNonReentrant modifier placement and correctness, ERC-777/callback token reentrancy via ReentrantMockToken, UUPS upgrade authorization logic in WellUpgradeable, Access control on upgradeTo and upgradeToAndCall, Stable2.decodeWellData logic correctness, MultiFlowPump.update storage slot collision analysis, Aquifer.boreWell reentrancy protection, Read-only reentrancy on view functions (getSwapOut, getSwapIn, getReserves, etc.) via readOnlyNonReentrant, ERC-4626-style mint/burn ordering in addLiquidity and removeLiquidity, Flash loan callback patterns (none present), ERC-1155 callback patterns (none present) |
| access control | success | 4 | 1M | 84% | 42.2s | Access control on upgradeTo/upgradeToAndCall in WellUpgradeable, _authorizeUpgrade logic and ownership enforcement, Initializer protection: disableInitializers in Well constructor, reinitializer in WellUpgradeable, initNoWellToken flow and partial initialization risk, Stable2.decodeWellData logic for decimal defaulting, Aquifer.boreWell reentrancy and initialization checks, Well.skim, sync, shift access control (permissionless by design), Signature replay in ERC20Permit (standard OZ pattern), delegatecall usage in LibClone (standard clone pattern), tx.origin usage (none found), ecrecover usage (none found in Well/WellUpgradeable - handled by OZ ERC20Permit), MockToken.setCall / ReentrantMockToken reentrancy vector (test-only contracts), MockTokenFeeOnTransfer.setFee unprotected access (test-only contract), MultiFlowPump update caller authentication (uses msg.sender as storage key - by design), Reserve storage slot collision risk in LibBytes, UUPS upgrade flow and proxiableUUID notDelegatedOrIsMinimalProxy modifier |
| economic | success | 5 | 1M2L | 85% | 58.1s | Flash loan attack surface on swap, addLiquidity, removeLiquidity functions in Well.sol, Oracle manipulation via spot reserve pricing in MultiFlowPump EMA/TWAP calculations, Chainlink/Pyth/Chronicle oracle integrations - none present, no external price feeds used, Governance attack surface - no governance token or voting mechanism present, Sandwich and MEV exposure - slippage protection via minAmountOut/maxAmountIn parameters, Reward and supply manipulation - first-depositor inflation attack surface in addLiquidity, Fee-on-transfer token handling in swapFromFeeOnTransfer and addLiquidityFeeOnTransfer, UUPS upgrade safety in WellUpgradeable - _authorizeUpgrade logic, Reentrancy protection via ReentrancyGuardUpgradeable and readOnlyNonReentrant modifier, Integer overflow/underflow in Well function math (ConstantProduct, Stable2), Stable2 decodeWellData decimal handling bug, Stable2 convergence loop return value handling, MultiFlowPump cap reserve logic and timestamp handling, Aquifer boreWell access control and initialization validation, LibBytes storage packing/unpacking correctness, Clone immutable args reading (ClonePlus/Clone), ERC20Permit integration for LP tokens, LP token first-depositor inflation (no virtual shares mechanism present) |
| logic validation | success | 7 | 1M1L | 79% | 1.4m | Input validation on all public/external functions (zero checks, bounds, array lengths), Arithmetic safety in Stable2 (getBandC division-before-multiplication, calcReserve Newton-Raphson), decodeWellData copy-paste bug (decimal0 checked twice instead of decimal1), Convergence loop termination in calcReserveAtRatioSwap and calcReserveAtRatioLiquidity, WellUpgradeable access control on upgradeTo/_authorizeUpgrade, Aquifer.boreWell initFunctionCall arbitrary execution and registry checks, EIP-712 domain separator caching (ERC20PermitUpgradeable used — relies on OZ, which is safe), abi.encodePacked with dynamic types in LibWellConstructor (tokens array padded to 32 bytes each — no collision risk), ReentrancyGuard usage across Well operations, MultiFlowPump.update reserve capping logic and slot calculation, LibBytes packing/unpacking correctness, UUPS upgrade pattern correctness with ERC-1167 minimal proxy adaptation, Pump failure isolation (try/catch pattern), Fee-on-transfer token handling in swapFromFeeOnTransfer and addLiquidityFeeOnTransfer |
| code quality | success | 7 | 84% | 1.1m | ERC-20 and ERC-2612 conformance (Well, WellUpgradeable), ERC-1967 / UUPS proxy upgrade safety (WellUpgradeable), Access control on upgrade functions, Reentrancy protection (Well, WellUpgradeable), Stable2 math correctness (decodeWellData null-check bug, convergence, scaling), Integer overflow/underflow in LibBytes, LibMath, ABDKMathQuad, Aquifer boreWell permissionlessness and init safety, MultiFlowPump storage collision and slot calculation, Fee-on-transfer token handling, Pump update failure handling, Mock token reentrancy patterns, LibBytes packing/unpacking correctness, Constant product and ConstantProduct2 math, UUPS proxiableUUID and _authorizeUpgrade logic, Deadline/expiry checks, Slippage protection | |
| compiler bugs | success | 3 | 86% | 42.5s | UUPS upgrade authorization logic in WellUpgradeable, Access control on upgradeTo/upgradeToAndCall, Stable2 decodeWellData decimal defaulting logic, Well reentrancy guards and readOnlyNonReentrant modifier, Pump failure handling in _updatePumps (try/catch pattern), Reserve storage packing in LibBytes, Fee-on-transfer token handling in swapFromFeeOnTransfer and addLiquidityFeeOnTransfer, Aquifer boreWell permissionless deployment and registry, MultiFlowPump cap logic and reserve manipulation resistance, ERC20 permit and initialization patterns, Integer overflow risks in Well function math (ConstantProduct, ConstantProduct2, Stable2), Compiler bug patterns - pragma ^0.8.20 is outside all affected ranges, Deadline expiry modifier correctness | |
| assembly safety | success | 4 | 1M2L | 80% | 51.5s | Full source codepoint-by-codepoint scan for non-ASCII characters (RTLO U+202E, zero-width joiners, Cyrillic homoglyphs) — none found, All assembly{} blocks in LibBytes, LibBytes16, LibLastReserveBytes, LibMath, LibClone, LibContractInfo, Clone, ClonePlus, MultiFlowPump — checked for sload/sstore patterns, free-memory-pointer overwrites, shift argument ordering, return vs leave, delegatecall safety, WellUpgradeable upgrade access control (_authorizeUpgrade, upgradeTo, upgradeToAndCall), Stable2.decodeWellData decimal defaulting logic, Aquifer.boreWell returnData pointer arithmetic, MultiFlowPump storage slot derivation (_getSlotForAddress), Well.sol core swap, liquidity, and reentrancy logic, ERC20/ERC20Permit standard compliance in MockToken, MockTokenFeeOnTransfer, ReentrancyGuardUpgradeable usage and readOnlyNonReentrant modifier, Integer overflow/underflow in ConstantProduct, ConstantProduct2, Stable2 math, LibMath.mulDivOrMax and roundUpDiv correctness, ABDKMathQuad library — internal math, no external calls, Pump failure isolation (try/catch in _updatePumps), Salt frontrunning protection in Aquifer |
| l2 specific | success | 7 | 1H1M3L | 77% | 1.3m | WellUpgradeable upgrade authorization and access control, Well swap, addLiquidity, removeLiquidity functions for CEI violations, Stable2 pricing function math and decodeWellData logic, MultiFlowPump update and reserve capping logic, Aquifer boreWell deployment and initialization flow, Reentrancy guards on Well functions, Slippage checks in swap and liquidity functions, LibBytes storage packing for reserve storage, ERC20 permit and upgradeable initialization, Cross-function reentrancy via readOnlyNonReentrant modifier, Clone/ClonePlus immutable argument reading, ABDKMathQuad floating point library usage, Fee-on-transfer token handling, Pump update failure handling (try/catch) |
| upgrade | success | 5 | 1H1M1L | 80% | 58.7s | UUPS upgrade authorization in WellUpgradeable._authorizeUpgrade, Access control on upgradeTo and upgradeToAndCall, Initialization safety: disableInitializers in Well constructor, initNoWellToken and init reinitializer version conflicts, notDelegatedOrIsMinimalProxy modifier logic, Storage layout: Well uses fixed RESERVES_STORAGE_SLOT and immutable args via Clone pattern, Stable2.decodeWellData for copy-paste bugs, Aquifer.boreWell init failure error handling, Reentrancy protection via ReentrancyGuardUpgradeable and readOnlyNonReentrant, MultiFlowPump storage keyed by msg.sender address slot, LibBytes storage packing for reserves, selfdestruct and delegatecall to untrusted addresses, ERC20 token handling and fee-on-transfer support, Pump update failure isolation (try/catch pattern), Deadline expiry modifier |
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/d41154e5-0a64-453d-89aa-f67f005f8cf7)
<a href="https://walletguard.ai/audit/d41154e5-0a64-453d-89aa-f67f005f8cf7"> <img src="https://walletguard.ai/api/badge/d41154e5-0a64-453d-89aa-f67f005f8cf7" alt="WalletGuard Audit Badge" /> </a>