feature: V3Provider#151
Conversation
… fork Remove scattered LICENSE files under dex/v3/core/ and add a single NOTICE file with proper attribution. Transition expired BUSL-1.1 headers to GPL-2.0-or-later and fix GPL-3.0 incompatibility in periphery libraries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the ~9.4k-LoC local fork under src/dex/v3 (a Uniswap-v3 fork ported to 0.8.34) and depend on github.com/lista-dao/lista-v3 instead. lista-v3 master is Solidity 0.7.6, so it cannot be compiled into this 0.8.34 codebase wholesale. Split by what actually compiles under 0.8.34: - Pure interfaces (IListaV3Factory, IListaV3Pool, pragma >=0.5.0) now come from the lib/lista-v3 submodule via the `lista-v3/` remapping. - The V3 math libs V3Provider executes internally (TickMath, SqrtPriceMath, LiquidityAmounts, FullMath, FixedPoint96, SafeCast, UnsafeMath) are 0.7.6 upstream and need 0.8.x unchecked semantics, so the existing 0.8.34 ports are relocated to src/provider/libraries/. - INonfungiblePositionManager: the submodule's interface inherits OZ ERC721-upgradeable interfaces whose v4-era paths/names don't exist in this repo's OZ v5.2, so a minimal self-contained NPM interface (only the methods V3Provider calls) lives in src/provider/interfaces/. Delete test/dex/v3/ListaV3.t.sol (tested the fork implementation itself; the DEX is tested in the lista-v3 repo) and repoint the IListaV3Pool import in V3Provider/V3Liquidator tests to the submodule. The unrelated StableSwap DEX under src/dex/*.sol is untouched. forge build: compiler run successful. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the 7 locally-vendored 0.8.34 math-lib ports under
src/provider/libraries/ with the audited, 0.8-native libraries from the
lista-dao-contracts submodule (pinned at 3ac9ef2, matching master).
- Register lib/lista-dao-contracts.git as a submodule + `lista-dao-contracts/`
remapping. The entry mirrors origin/master exactly, so it is conflict-free
when this branch later integrates with master.
- V3Provider imports TickMath and LiquidityAmounts from the submodule.
- _getAmountsForLiquidity now delegates to LiquidityAmounts.getAmountsForLiquidity,
which is mathematically identical to the previous SqrtPriceMath.getAmount{0,1}Delta
(roundUp=false) path. This removes the need for SqrtPriceMath (which
lista-dao-contracts does not ship a 0.8 version of) and its deps.
- Delete all 7 local ports: TickMath, SqrtPriceMath, LiquidityAmounts, FullMath,
FixedPoint96, SafeCast, UnsafeMath.
Verified: forge build clean; 84 V3Provider + 27 V3Liquidator tests pass
(covering preview/withdraw/rebalance across below/inside/above-range, which
exercises the swapped getAmountsForLiquidity path).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve conflicts in .gitmodules and remappings.txt by unioning both sides: - .gitmodules: keep our lib/lista-v3 submodule alongside master's existing lib/lista-dao-contracts.git entry. - remappings.txt: keep our lista-v3/ and lista-dao-contracts/ remappings plus master's lista-dao-contracts.git OZ-upgradeable remappings. forge build clean; 84 V3Provider tests pass on the merged tree.
…-rate oracle Rename V3Provider → SlisBNBV3Provider and split into a generic abstract base (V3Provider) + slisBNB/BNB specialization, per the slisBNB/BNB v3 LP PRD. - Base/derived split via virtual hooks (_afterCollateralChange, _rebalanceInventory, _valuationSqrtPriceX96, peek/getTokenConfig/receive). - ERC-4626 shell: asset = WBNB, totalAssets() in BNB; single-asset entry disabled (two-token deposit/withdraw/redeemShares + withdrawShares/supplyShares). - Exchange-rate oracle (PRD §4.5): peek/totalAssets/getUserBalanceInBnb value the position at the slisBNB exchange-rate-implied price (StakeManager convertSnBnbToBnb/convertBnbToSnBnb), never the pool spot/TWAP — manipulation-resistant. - Auto-centered range: initialize derives ticks from exchangeRate ±1%; rebalance recenters and converts inventory via the StakeManager (deposit / instantWithdraw). - Libraries: V3PositionLib (NPM primitives) + SlisBnbInventoryLib (stake/redeem + optimal-ratio conversion); audited 0.8 math from lista-dao-contracts. - require strings → custom errors; AccessControl (non-enumerable). - Tests: SlisBNBV3Provider.t.sol (USDC/WBNB generic) + SlisBNBV3ProviderRate.t.sol (slisBNB/WBNB rate-path, forked). Note: runtime bytecode currently exceeds EIP-170; size-reduction refactor pending. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…+ oracle Split the monolithic SlisBNBV3Provider (28.5 KB, over EIP-170) into three UUPS contracts, each well under the limit, with clean responsibility boundaries: - V3Provider / SlisBNBV3Provider: ERC-4626 vLP shares + Moolah wiring + share accounting (holds no NFT); slisBNB subclass adds slisBNBx mirroring + BOT rebalance. - V3DexAdapter / SlisBNBV3DexAdapter: sole V3 NFT/idle custodian + all NPM/pool math; slisBNB subclass adds the exchange-rate-implied fair price, +/-1% tick centering, and the rate-centered rebalance + StakeManager inventory conversion. - SlisBNBV3ProviderOracle: Moolah market.oracle; prices the share off the adapter's fair composition (manipulation-resistant) + resilient oracle, capped haircut. Key points: - CEI: burn shares BEFORE adapter.removeLiquidity pushes underlying to the receiver, so totalSupply stays consistent with the reduced position during the BNB callback. - Oracle reverts on zero leg price / zero total value (finding D); raw-NAV vs haircut split is clean (adapter returns raw, oracle applies the only haircut). - slisBNB/BNB-only: adapter and oracle constructors reject any non-slisBNB/WBNB pair; the oracle also asserts its tokens match the adapter's (no wiring divergence). - IV3PoolMinimal: decode only slot0 sqrtPriceX96/tick, width-agnostic to feeProtocol (Uniswap uint8 vs PancakeSwap uint32) so the adapter works against any V3 fork. - Tests retargeted to the slisBNB/WBNB pool: functional 87, rate-path 8, liquidator 27 (etched StakeManager stand-in for the not-yet-deployed instantWithdraw). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…matting - foundry.lock: remove orphaned lib/v3-core and lib/v3-periphery entries (superseded by lista-v3; no .gitmodules entry, lib dir, remapping, or import). - Apply prettier formatting to the V3 vault/adapter contracts and the liquidator + rate-path tests (npm run check now passes). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tracts Switch V3Provider, V3DexAdapter and SlisBNBV3ProviderOracle from plain AccessControlUpgradeable to AccessControlEnumerableUpgradeable, matching the codebase standard (Moolah, SmartProvider, et al.) so roles are enumerable on-chain via getRoleMember/getRoleMemberCount. Existing role logic (grantRole/hasRole/onlyRole/getRoleAdmin) is unchanged; all three stay under EIP-170. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…quidator Match the V3 provider contracts and the codebase standard so roles are enumerable on-chain (getRoleMember/getRoleMemberCount). Existing role logic is unchanged; V3Liquidator stays well under EIP-170. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
V3Provider.deposit forwarded the unused-input refund to the depositor via a native-BNB call inside adapter.addLiquidity, BEFORE _mint + supplyCollateral. During that callback the adapter NAV already included the freshly-added liquidity while totalSupply() was still stale, so SlisBNBV3ProviderOracle.peek returned a transiently inflated share price (26x on a BSC fork). Moolah was not yet reentrancy-locked at that point, letting a malicious depositor reenter Moolah.borrow and over-borrow against pre-existing collateral, leaving bad debt. Fix: the adapter now refunds to the vault, and the vault forwards the refund to the depositor only AFTER _mint + supplyCollateral, when NAV and totalSupply are consistent. Adds a guarded receive() (adapter-only) + _refund helper. Regression: test_C1_depositRefundReentrancy_neutralized drives the full attack and asserts peek during the refund equals the normal price and the attacker position stays solvent. Fails pre-fix (26x inflation, insolvent), passes post-fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…reentrancy fix(provider): close deposit-refund reentrancy (C-1)
|
@audit-agent V3Liquidator.sol V3DexAdapter.sol V3Provider.sol SlisBNBV3DexAdapter.sol SlisBNBV3Provider.sol SlisBNBV3ProviderOracle.sol SlisBnbInventoryLib.sol V3PositionLib.sol |
🛡️ Cloud-Auditor — PR #151 reviewDispatched: 9 specialists · Head SHA: Tally
Top findingsH-01 — H-02 — M-01 — M-02 — Resilient-oracle TOKEN0/TOKEN1 legs must be rate-derived for the pool-manipulation invariant to hold M-03 — M-04 — M-05 — Lows (9)L-01 Missing Acknowledged / verified safe
Full report (PDF) delivered to the security Telegram group. |
…ETH/wbETH Generalize the slisBNB V3 LP provider into a chain/LST-agnostic base and add Ethereum wstETH/WETH and wbETH/WETH markets. - Base: chain/LST-agnostic V3Provider / V3DexAdapter / V3ProviderOracle (WRAPPED_NATIVE immutable; virtual _lstNativeRate() / _convertToOptimalRatio() hooks). slisBNB becomes a thin, byte-equivalent subclass. All V3 contracts relocated under src/provider/v3/. - New LST families: wstETH/WETH and wbETH/WETH adapters + vaults + IWstETH/IWbETH. - Oracle: rate-implied (slisBNB) / pool-TWAP-clamped-to-rate (wstETH/wbETH) share pricing; components valued rate-derived via the resilient oracle. - Rebalance swap is DEX-agnostic: backend-built swapData against a whitelisted swapPair (SwapInventoryLib generic swap, backend amountOutMin); drop SwapRouter02 coupling (delete ISwapRouter02). - V3Liquidator: chain-agnostic native leg via provider.WRAPPED_NATIVE() for both legs; wrap native back when loanToken == wrapped-native; reject sensitive swapPair (token/pool/NPM). - Cross-validate provider<->adapter<->oracle wiring in setProvider + oracle ctor. Tests: 245 passing — BSC slisBNB/liquidator regression unchanged + Ethereum fork suites for wstETH, wbETH and V3Liquidator (incl. native-leg + wiring guards). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…onversion Add maxInstantWithdrawSlippageBps to SlisBnbInventoryLib so the slisBNB→BNB instantWithdraw leg of a rebalance enforces a bound on the StakeManager instant-redeem fee/slippage, reverting InstantWithdrawSlippage when exceeded. Threaded through convertToOptimalRatio/convert/_convert; SlisBNBV3DexAdapter passes the configured bound. Adds tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pull Request ReviewThis PR introduces a new V3 collateral-provider architecture for concentrated liquidity positions by adding multiple new Solidity contracts: a generic Sensitive ContentBlockchain Address:
Security Issues🟡 [MEDIUM] Input validation relaxed in
Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits. |
Pull Request ReviewThis PR introduces a new V3 concentrated-liquidity provider architecture for slisBNB/WBNB by adding multiple new Solidity components: Sensitive ContentBlockchain Address:
Security IssuesNo serious security issues detected. Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits. |
Feature: eth v3 lp
Pull Request ReviewThis PR introduces a major new Solidity web3 architecture for V3 concentrated-liquidity collateral: a split between Sensitive ContentBlockchain Address:
No sensitive key material or social account handles/links detected in non-test added files. Security IssuesNo serious security issues detected. Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits. |
…nostic swap) Make the slisBNB/BNB rebalance inventory conversion identical to the wstETH/WETH and wbETH/WETH families: a backend-built swap against a whitelisted venue, swap-pair-agnostic on chain. instantWithdraw is conceptually just a swap, so the StakeManager special-casing is no longer needed — promoting one shared implementation also simplifies the code. - Promote the generic conversion to the base V3DexAdapter: swapPairWhitelist + setSwapPairWhitelist (rejecting TOKEN0/TOKEN1/POOL/NPM) + the decode-and-swap _convertToOptimalRatio (via SwapInventoryLib, backend amountOutMin). All three rate-implied subclasses inherit it; both declared on IV3DexAdapter. - SwapInventoryLib is native-aware: when a venue settles the wrapped-native leg as the native coin (e.g. a StakeManager instant-redeem → BNB), the received native is wrapped back into the wrapped-native ERC20 before amountOutMin is measured; receive() accepts native from a whitelisted venue. ⇒ instantWithdraw works as a whitelisted swap venue (slisBNB→BNB→WBNB), with no StakeManager special-casing on chain. - Slim SlisBNBV3DexAdapter to _lstNativeRate() (StakeManager rate) + the pair guard; drop the StakeManager conversion override, the instant-withdraw slippage config, and the StakeManager receive() override. Delete SlisBnbInventoryLib. - Slim WstETH/WbETHV3DexAdapter: swap logic now in the base; keep rate hook + TWAP-clamp. - SlisBNBV3Provider.rebalance gains bytes calldata swapData, forwarded to the adapter. Behavioral change: slisBNB rebalance converts via a whitelisted venue (a DEX/aggregator OR the StakeManager instant-redeem) using backend swapData + amountOutMin, instead of a hardcoded path. Tests: 257 passing — slisBNB swap-conversion (instantWithdraw-as-venue, both directions, amountOutMin-revert) + whitelist + sensitive-pair-reject; old StakeManager-special-case/slippage tests removed; wstETH/wbETH/liquidator/reentrancy green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Build on the unified DEX-agnostic rebalance swap (9ba9766) so the V3 LP base is correct for native-settling venues and non-18-decimal tokens. - SwapInventoryLib native-aware both directions: the swapData blob gains bool nativeIn — now (swapPair, sellToken0, amountIn, amountOutMin, nativeIn, innerSwapData). nativeIn ⇒ the wrapped-native input leg is unwrapped and forwarded as msg.value (supports StakeManager.deposit{value} for WBNB→slisBNB). Any native a venue delivers (e.g. instantWithdraw → BNB) is wrapped back into the wrapped-native ERC20 before amountOutMin is measured — never stranded; reverts UnexpectedNative if neither leg is the wrapped-native. - Non-18-decimal paired token: _sqrtPriceX96FromRate now scales the raw pool price by 10^(DECIMALS1 - DECIMALS0) instead of assuming equal decimals (pure→view; cascaded to _tickRangeForRate). The wrapped-native stays 18-dec; the paired token may have any decimals. - Oracle share-decimal correctness: V3ProviderOracle quotes peek(share) per ONE WHOLE share (10 ** SHARE_DECIMALS, read from the share token) instead of hardcoded 1e18, matching Moolah._getPrice which uses collateralToken.decimals(). No-op for the current 18-dec accounting assets. Tests: 263 passing — new V3DexAdapterDecimals.t.sol (non-18-dec USDC(6)/WETH(18) + 18/18) proves the sqrtPrice decimal math; slisBNB depositAsSwapVenue (nativeIn), wrongNativeOutput_reverts, instantWithdraw no-stranded-native; swapData blobs updated to the 6-field shape. Backend encoder must add nativeIn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
refactor(provider): unify slisBNB rebalance with wstETH/wbETH (DEX-ag…
Pull Request ReviewThis PR introduces a new V3 concentrated-liquidity provider architecture for Moolah collateral by adding a large set of Solidity contracts: Sensitive ContentBlockchain Address:
Security Issues🟡 [MEDIUM] Input validation relaxed in
Generated by Hashdit Bot. This tool can absolutely NOT replace manual audits. |
Summary
A Lista V3 concentrated-liquidity collateral provider for the slisBNB/BNB pair, used as Moolah collateral. The original monolith exceeded EIP-170, so it is split into three UUPS contracts with clean boundaries, plus a dedicated liquidator:
market.oracle. Prices the share off the adapter's manipulation-resistant fair composition + the resilient oracle, with a capped haircut.Architecture (3-contract split)
V3Provider(vault)V3DexAdapter(strategy)SlisBNBV3ProviderOracle(oracle)The vault never touches the NFT/pool; the adapter never imports Moolah or the share token (it takes
(shares, totalShares)as opaque fractions); the oracle reads only public views. Raw-NAV vs haircut is cleanly separated (adapter returns raw, oracle applies the only haircut).Key Design Decisions
NotSlisBnbWbnbPair); the oracle additionally asserts its tokens match the adapter's (AdapterPairMismatch), so a mis-wired deploy can't silently misprice.convertSnBnbToBnb), not pool spot/TWAP —peek/totalAssets/getUserBalanceInBnbare invariant to pool-price manipulation. Per-leg USD comes from the resilient oracle;peekreverts on a zero leg price or zero total value (finding D);supply == 0returns 0(pre-market).
shares = addedValue × supplyBefore / totalValueBefore, valuing freshly-added liquidity at the fair price (first deposit denominated in the accounting asset, WBNB). Fees are compounded before minting so existing holders capture them before dilution.deposit/instantWithdraw, measured by balance delta — not pool-manipulable), guarded by a configurable rate-drift threshold (centerRateThresholdBps) +deadline+minLiquidity. BOT-gated on the vault;onlyProvideron the adapter._mint+supplyCollateral(CEI), so the native-BNB refund callback can't observe an inflated share price (staletotalSupplyvs. already-added NAV) and reenterMoolah.borrow. Withdraw/redeem likewise burn before the adapter pushesunderlying to the receiver.
idleToken0/idleToken1storage (notbalanceOf) so donations can't inflate NAV.msg.value(auto-wrapped to WBNB); withdrawals unwrap WBNB to native BNB.AccessControlEnumerableUpgradeableacross vault/adapter/oracle/liquidator (codebase standard; enumerable roles).IV3PoolMinimalslot0 read — decodes onlysqrtPriceX96/tick, width-agnostic tofeeProtocol(Uniswapuint8vs PancakeSwapuint32), so the adapter works against any V3 fork.lista-dao-contractslibs; interfaces from thelista-v3submodule.Files
src/provider/V3Provider.solsrc/provider/SlisBNBV3Provider.solsrc/provider/V3DexAdapter.solsrc/provider/SlisBNBV3DexAdapter.solsrc/provider/SlisBNBV3ProviderOracle.solmarket.oracle— share pricing off fair view + haircutsrc/provider/interfaces/*.solIV3Provider,IV3DexAdapter,ISlisBNBV3DexAdapter,IV3ProviderOracle,IV3PoolMinimal,IStakeManagersrc/provider/libraries/V3PositionLib.solsrc/provider/libraries/SlisBnbInventoryLib.solsrc/liquidator/V3Liquidator.soltest/provider/SlisBNBV3Provider.t.soltest/provider/SlisBNBV3ProviderRate.t.soltest/provider/V3ProviderReentrancyPoC.t.soltest/liquidator/V3Liquidator.t.solTest Plan
All suites fork BSC mainnet at block 60541406 (slisBNB/WBNB 1bp pool; the live StakeManager is etched with a faithful
instantWithdrawstand-in for the rebalance conversion).SlisBNBV3Provider.t.sol) — deposit/withdraw/redeem/supplyShares/withdrawShares, value-based share math, previews, oraclepeek/getTokenConfig+ finding-D, one-sided & slippage guards, transfer restrictions, slisBNBx integration, rebalance (incl. out-of-range via inventory conversion), borrow/repay/liquidate.SlisBNBV3ProviderRate.t.sol) —peek/totalAssets/getUserBalanceInBnbinvariant to pool manipulation; rate-centered rebalance + drift/deadline/minLiquidity guards. (8 tests)V3Liquidator.t.sol) — pre-funded + flash liquidation, share redemption, token/BNB swaps, whitelist/access control. (27 tests)V3ProviderReentrancyPoC.t.sol) —test_C1_depositRefundReentrancy_neutralizeddrives the full attack; fails pre-fix (≈26× inflation, insolvent), passes post-fix.