|
| 1 | +import { |
| 2 | + AaveV3Arbitrum, |
| 3 | + AaveV3Avalanche, |
| 4 | + AaveV3Base, |
| 5 | + AaveV3BNB, |
| 6 | + AaveV3Ethereum, |
| 7 | + AaveV3EthereumEtherFi, |
| 8 | + AaveV3EthereumLido, |
| 9 | + AaveV3Gnosis, |
| 10 | + AaveV3Linea, |
| 11 | + AaveV3Optimism, |
| 12 | + AaveV3Plasma, |
| 13 | + AaveV3Polygon, |
| 14 | + AaveV3Sonic, |
| 15 | +} from '@aave-dao/aave-address-book'; |
| 16 | +import { SupportedChainId } from '@cowprotocol/cow-sdk'; |
| 17 | +import { useQuery } from '@tanstack/react-query'; |
| 18 | +import { Contract } from 'ethers'; |
| 19 | +import { CustomMarket, MarketDataType } from 'src/ui-config/marketsConfig'; |
| 20 | +import { getProvider } from 'src/utils/marketsAndNetworksConfig'; |
| 21 | + |
| 22 | +import { ADAPTER_FACTORY, FLASH_LOAN_FEE_BPS } from '../constants/cow.constants'; |
| 23 | +import { PARASWAP_FLASH_LOAN_FEE_BPS } from '../constants/paraswap.constants'; |
| 24 | +import { SwapProvider, SwapType } from '../types'; |
| 25 | + |
| 26 | +const ACL_MANAGER_ABI = ['function isFlashBorrower(address) view returns (bool)']; |
| 27 | + |
| 28 | +// CoW per-market ACLManager addresses. Only CoW flows do the on-chain lookup — |
| 29 | +// Paraswap flows always have the user EOA as msg.sender to Pool.flashLoan, so |
| 30 | +// they pay the default premium and don't need a check. |
| 31 | +const ACL_MANAGER_BY_MARKET: Partial<Record<CustomMarket, string>> = { |
| 32 | + [CustomMarket.proto_mainnet_v3]: AaveV3Ethereum.ACL_MANAGER, |
| 33 | + [CustomMarket.proto_lido_v3]: AaveV3EthereumLido.ACL_MANAGER, |
| 34 | + [CustomMarket.proto_etherfi_v3]: AaveV3EthereumEtherFi.ACL_MANAGER, |
| 35 | + [CustomMarket.proto_arbitrum_v3]: AaveV3Arbitrum.ACL_MANAGER, |
| 36 | + [CustomMarket.proto_base_v3]: AaveV3Base.ACL_MANAGER, |
| 37 | + [CustomMarket.proto_polygon_v3]: AaveV3Polygon.ACL_MANAGER, |
| 38 | + [CustomMarket.proto_optimism_v3]: AaveV3Optimism.ACL_MANAGER, |
| 39 | + [CustomMarket.proto_avalanche_v3]: AaveV3Avalanche.ACL_MANAGER, |
| 40 | + [CustomMarket.proto_sonic_v3]: AaveV3Sonic.ACL_MANAGER, |
| 41 | + [CustomMarket.proto_gnosis_v3]: AaveV3Gnosis.ACL_MANAGER, |
| 42 | + [CustomMarket.proto_bnb_v3]: AaveV3BNB.ACL_MANAGER, |
| 43 | + [CustomMarket.proto_linea_v3]: AaveV3Linea.ACL_MANAGER, |
| 44 | + [CustomMarket.proto_plasma_v3]: AaveV3Plasma.ACL_MANAGER, |
| 45 | +}; |
| 46 | + |
| 47 | +/** |
| 48 | + * Resolve the flashloan fee bps for the active swap. |
| 49 | + * |
| 50 | + * - CoW: msg.sender to Pool.flashLoan is the CoW factory. Check |
| 51 | + * ACLManager.isFlashBorrower(factory) on chain. Returns 0 when whitelisted, |
| 52 | + * FLASH_LOAN_FEE_BPS when not, and `undefined` while the query is pending or |
| 53 | + * when we can't run the check (factory or ACLManager not mapped for the |
| 54 | + * chain). Submit handlers MUST refuse to send while the value is undefined. |
| 55 | + * - Paraswap: msg.sender is always the user EOA, so the role can never apply. |
| 56 | + * Returns the constant immediately, no on-chain call. |
| 57 | + */ |
| 58 | +export const useFlashLoanFeeBps = ({ |
| 59 | + provider, |
| 60 | + swapType, |
| 61 | + marketData, |
| 62 | +}: { |
| 63 | + provider: SwapProvider; |
| 64 | + swapType: SwapType; |
| 65 | + marketData: MarketDataType; |
| 66 | +}): number | undefined => { |
| 67 | + const isCow = provider === SwapProvider.COW_PROTOCOL; |
| 68 | + const aclManager = ACL_MANAGER_BY_MARKET[marketData.market]; |
| 69 | + const target = isCow |
| 70 | + ? ADAPTER_FACTORY[marketData.chainId as unknown as SupportedChainId] || undefined |
| 71 | + : undefined; |
| 72 | + |
| 73 | + const enabled = isCow && Boolean(aclManager && target); |
| 74 | + |
| 75 | + const { data: isWhitelisted } = useQuery({ |
| 76 | + queryFn: async (): Promise<boolean> => { |
| 77 | + const contract = new Contract( |
| 78 | + aclManager as string, |
| 79 | + ACL_MANAGER_ABI, |
| 80 | + getProvider(marketData.chainId) |
| 81 | + ); |
| 82 | + return contract.isFlashBorrower(target); |
| 83 | + }, |
| 84 | + queryKey: ['flashBorrowerCheck', marketData.chainId, aclManager, target], |
| 85 | + enabled, |
| 86 | + staleTime: 5 * 60 * 1000, |
| 87 | + }); |
| 88 | + |
| 89 | + if (!isCow) { |
| 90 | + // Acknowledge swapType to keep the dep narrow even though Paraswap |
| 91 | + // doesn't branch on it for fee resolution. |
| 92 | + void swapType; |
| 93 | + return PARASWAP_FLASH_LOAN_FEE_BPS; |
| 94 | + } |
| 95 | + if (!enabled) return undefined; |
| 96 | + if (isWhitelisted === undefined) return undefined; |
| 97 | + return isWhitelisted ? 0 : FLASH_LOAN_FEE_BPS; |
| 98 | +}; |
0 commit comments