@@ -14,26 +14,45 @@ import {
1414import { isProtocolSwapState } from '../../types/state.types' ;
1515import { InsufficientLiquidityBlockingError } from './InsufficientLiquidityBlockingError' ;
1616
17+ // The cow-swap-adapter flash-loans `_sellToken` for every position swap
18+ // (CollateralSwap / DebtSwap / RepayWithCollateral, see
19+ // cow-swap-adapters/src/adapters/v3/*Adapter.sol). The interface exposes that
20+ // asset as `state.sellAmountToken`, so checking against it sidesteps the
21+ // source/destination + isInvertedSwap question and mirrors what actually pulls
22+ // from the lender on-chain.
1723export const hasInsufficientLiquidity = ( state : SwapState ) => {
18- // Only relevant for Debt Swaps where target asset availability and borrow cap matter.
19- // Collateral-related flows are handled via SupplyCapBlockingGuard and should not use borrow caps here.
20- if ( ! isProtocolSwapState ( state ) || state . swapType !== SwapType . DebtSwap ) return false ;
21- const reserve = state . isInvertedSwap
22- ? state . sourceReserve ?. reserve
23- : state . destinationReserve ?. reserve ;
24- const buyAmount = state . buyAmountFormatted ;
25- if ( ! reserve || ! buyAmount ) return false ;
24+ // isProtocolSwapState narrows out SwapType.Swap (direct DEX, no Aave call).
25+ if ( ! isProtocolSwapState ( state ) ) return false ;
26+ // Don't gate on `state.useFlashloan`: several protocol paths flash-loan
27+ // unconditionally regardless of the flag (CoW DebtSwap / RepayWithCollateral
28+ // via forceFlashloanFlow, ParaSwap DebtSwap via DebtSwitchAdapter, ParaSwap
29+ // CollateralSwap with `useFlashLoan: true` hardcoded). And even non-
30+ // flashloan paths still withdraw/borrow from the pool, which decrements
31+ // virtualUnderlyingBalance — the same liquidity ceiling we're guarding.
32+ if ( ! state . sellAmountToken || ! state . sellAmountFormatted ) return false ;
2633
27- const availableBorrowCap =
28- reserve . borrowCap === '0'
29- ? valueToBigNumber ( ethers . constants . MaxUint256 . toString ( ) )
30- : valueToBigNumber ( reserve . borrowCap ) . minus ( valueToBigNumber ( reserve . totalDebt ) ) ;
31- const availableLiquidity = BigNumber . max (
32- BigNumber . min ( valueToBigNumber ( reserve . formattedAvailableLiquidity ) , availableBorrowCap ) ,
33- 0
34+ const flashLoanedAddress = state . sellAmountToken . underlyingAddress ?. toLowerCase ( ) ;
35+ if ( ! flashLoanedAddress ) return false ;
36+
37+ const reserve = [ state . sourceReserve ?. reserve , state . destinationReserve ?. reserve ] . find (
38+ ( r ) => r ?. underlyingAsset ?. toLowerCase ( ) === flashLoanedAddress
3439 ) ;
40+ if ( ! reserve ) return false ;
41+
42+ const liquidity = BigNumber . max ( valueToBigNumber ( reserve . formattedAvailableLiquidity ) , 0 ) ;
43+
44+ // Borrow cap only matters for DebtSwap, which leaves the user holding new
45+ // debt in the flash-loaned asset. Other flash-loan flows repay the loan
46+ // in-flight and never touch the borrow cap.
47+ const borrowCapRoom =
48+ state . swapType === SwapType . DebtSwap
49+ ? reserve . borrowCap === '0'
50+ ? valueToBigNumber ( ethers . constants . MaxUint256 . toString ( ) )
51+ : valueToBigNumber ( reserve . borrowCap ) . minus ( valueToBigNumber ( reserve . totalDebt ) )
52+ : valueToBigNumber ( ethers . constants . MaxUint256 . toString ( ) ) ;
3553
36- return valueToBigNumber ( buyAmount ) . gt ( availableLiquidity ) ;
54+ const effectiveLimit = BigNumber . max ( BigNumber . min ( liquidity , borrowCapRoom ) , 0 ) ;
55+ return valueToBigNumber ( state . sellAmountFormatted ) . gt ( effectiveLimit ) ;
3756} ;
3857
3958export const InsufficientLiquidityBlockingGuard = ( {
@@ -82,16 +101,20 @@ export const InsufficientLiquidityBlockingGuard = ({
82101 }
83102 }
84103 } , [
85- state . buyAmountFormatted ,
86- state . destinationReserve ?. reserve ?. formattedAvailableLiquidity ,
104+ state . swapType ,
105+ state . sellAmountFormatted ,
106+ state . sellAmountToken ?. underlyingAddress ,
87107 state . sourceReserve ?. reserve ?. formattedAvailableLiquidity ,
88- state . isInvertedSwap ,
108+ state . sourceReserve ?. reserve ?. borrowCap ,
109+ state . sourceReserve ?. reserve ?. totalDebt ,
110+ state . destinationReserve ?. reserve ?. formattedAvailableLiquidity ,
111+ state . destinationReserve ?. reserve ?. borrowCap ,
112+ state . destinationReserve ?. reserve ?. totalDebt ,
89113 ] ) ;
90114
91115 if ( hasInsufficientLiquidity ( state ) ) {
92- const symbol = state . isInvertedSwap
93- ? state . sourceReserve ?. reserve ?. symbol
94- : state . destinationReserve ?. reserve ?. symbol ;
116+ // hasInsufficientLiquidity ensures sellAmountToken is defined.
117+ const symbol = state . sellAmountToken ?. symbol ?? '' ;
95118 return (
96119 < InsufficientLiquidityBlockingError
97120 symbol = { symbol }
0 commit comments