Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export const CollateralSwapActionsViaCowAdapters = ({
: undefined;

const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
sellAmount: state.sellAmountBigInt,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export const DebtSwapActionsViaCoW = ({
: undefined;

const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
sellAmount: BigInt(sellAmountWithMarginForDustProtection),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export const RepayWithCollateralActionsViaCoW = ({
: undefined;

const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
sellAmount: BigInt(sellAmountWithMarginForDustProtection),
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export const calculateInstanceAddress = async ({
};

const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
Comment thread
mgrabina marked this conversation as resolved.
Outdated
sellAmount: BigInt(sellAmountWithMarginForDustProtection),
});

Expand Down Expand Up @@ -180,7 +180,7 @@ export const calculateFlashLoanAmounts = (

const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
sellAmount: sellAmount,
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
});

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export const calculateParaswapFlashLoanFee = (

// Calculate fee: flashloan amount * fee bps / 10000
// The flashloan amount is the sell amount (collateral being swapped)
const flashLoanFeeAmount =
(state.sellAmountBigInt * BigInt(PARASWAP_FLASH_LOAN_FEE_BPS)) / BigInt(10000);
const feeBps = state.flashLoanFeeBps ?? PARASWAP_FLASH_LOAN_FEE_BPS;
const flashLoanFeeAmount = (state.sellAmountBigInt * BigInt(feeBps)) / BigInt(10000);

// Format the fee amount
const flashLoanFeeFormatted = valueToBigNumber(flashLoanFeeAmount.toString())
Expand Down
104 changes: 104 additions & 0 deletions src/components/transactions/Swap/hooks/useFlashLoanFeeBps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {
AaveV3Arbitrum,
AaveV3Avalanche,
AaveV3Base,
AaveV3BNB,
AaveV3Ethereum,
AaveV3EthereumEtherFi,
AaveV3EthereumLido,
AaveV3Gnosis,
AaveV3Optimism,
AaveV3Polygon,
AaveV3Sonic,
} from '@aave-dao/aave-address-book';
import { SupportedChainId } from '@cowprotocol/cow-sdk';
import { useQuery } from '@tanstack/react-query';
import { Contract } from 'ethers';
import { CustomMarket, MarketDataType } from 'src/ui-config/marketsConfig';
import { getProvider } from 'src/utils/marketsAndNetworksConfig';

import { ADAPTER_FACTORY, FLASH_LOAN_FEE_BPS } from '../constants/cow.constants';
import { PARASWAP_FLASH_LOAN_FEE_BPS } from '../constants/paraswap.constants';
import { SwapProvider, SwapType } from '../types';

const ACL_MANAGER_ABI = ['function isFlashBorrower(address) view returns (bool)'];

const ACL_MANAGER_BY_MARKET: Partial<Record<CustomMarket, string>> = {
[CustomMarket.proto_mainnet_v3]: AaveV3Ethereum.ACL_MANAGER,
[CustomMarket.proto_lido_v3]: AaveV3EthereumLido.ACL_MANAGER,
[CustomMarket.proto_etherfi_v3]: AaveV3EthereumEtherFi.ACL_MANAGER,
[CustomMarket.proto_arbitrum_v3]: AaveV3Arbitrum.ACL_MANAGER,
[CustomMarket.proto_base_v3]: AaveV3Base.ACL_MANAGER,
[CustomMarket.proto_polygon_v3]: AaveV3Polygon.ACL_MANAGER,
[CustomMarket.proto_optimism_v3]: AaveV3Optimism.ACL_MANAGER,
[CustomMarket.proto_avalanche_v3]: AaveV3Avalanche.ACL_MANAGER,
[CustomMarket.proto_sonic_v3]: AaveV3Sonic.ACL_MANAGER,
[CustomMarket.proto_gnosis_v3]: AaveV3Gnosis.ACL_MANAGER,
[CustomMarket.proto_bnb_v3]: AaveV3BNB.ACL_MANAGER,
};

const fallbackBps = (provider: SwapProvider): number =>
provider === SwapProvider.COW_PROTOCOL ? FLASH_LOAN_FEE_BPS : PARASWAP_FLASH_LOAN_FEE_BPS;

const resolveTarget = (
provider: SwapProvider,
swapType: SwapType,
marketData: MarketDataType
): string | undefined => {
if (provider === SwapProvider.COW_PROTOCOL) {
return ADAPTER_FACTORY[marketData.chainId as unknown as SupportedChainId] || undefined;
}
if (provider === SwapProvider.PARASWAP) {
switch (swapType) {
case SwapType.CollateralSwap:
return marketData.addresses.SWAP_COLLATERAL_ADAPTER;
case SwapType.RepayWithCollateral:
return marketData.addresses.REPAY_WITH_COLLATERAL_ADAPTER;
case SwapType.DebtSwap:
return marketData.addresses.DEBT_SWITCH_ADAPTER;
case SwapType.WithdrawAndSwap:
return marketData.addresses.WITHDRAW_SWITCH_ADAPTER;
default:
return undefined;
}
}
return undefined;
};

/**
* Resolve the flashloan fee bps for the active swap, defaulting to the
* provider's hardcoded bps when the on-chain ACLManager check is unavailable
* or pending. Returns 0 only when the target adapter (Paraswap per-flow) /
* factory (CoW) is registered as a FLASH_BORROWER on the market's ACLManager.
*/
export const useFlashLoanFeeBps = ({
provider,
swapType,
marketData,
}: {
provider: SwapProvider;
swapType: SwapType;
marketData: MarketDataType;
}): number => {
const aclManager = ACL_MANAGER_BY_MARKET[marketData.market];
const target = resolveTarget(provider, swapType, marketData);
const fallback = fallbackBps(provider);

const enabled = Boolean(aclManager && target);

const { data: isWhitelisted } = useQuery({
queryFn: async (): Promise<boolean> => {
const contract = new Contract(
aclManager as string,
ACL_MANAGER_ABI,
getProvider(marketData.chainId)
);
return contract.isFlashBorrower(target);
},
queryKey: ['flashBorrowerCheck', marketData.chainId, aclManager, target],
enabled,
staleTime: 5 * 60 * 1000,
});

return isWhitelisted === true ? 0 : fallback;
};
18 changes: 11 additions & 7 deletions src/components/transactions/Swap/hooks/useSwapOrderAmounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { OrderKind } from '@cowprotocol/cow-sdk';
import { Dispatch, useEffect } from 'react';
import { useRootStore } from 'src/store/root';

import { COW_PARTNER_FEE, FLASH_LOAN_FEE_BPS } from '../constants/cow.constants';
import { PARASWAP_FLASH_LOAN_FEE_BPS } from '../constants/paraswap.constants';
import { COW_PARTNER_FEE } from '../constants/cow.constants';
import {
isCowProtocolRates,
OrderType,
Expand All @@ -13,6 +12,7 @@ import {
SwapState,
SwapType,
} from '../types';
import { useFlashLoanFeeBps } from './useFlashLoanFeeBps';
import { swapTypesThatRequiresInvertedQuote } from './useSwapQuote';

const marketOrderKindPerSwapType: Record<SwapType, OrderKind> = {
Expand All @@ -27,10 +27,6 @@ const isPositionSwap = (swapType: SwapType, usingFlashloan: boolean) => {
return swapType != SwapType.Swap && usingFlashloan;
};

const getFlashLoanFeeBps = (provider: SwapProvider) => {
return provider === SwapProvider.COW_PROTOCOL ? FLASH_LOAN_FEE_BPS : PARASWAP_FLASH_LOAN_FEE_BPS;
};

/**
* Computes normalized sell/buy amounts used to build transactions.
*
Expand All @@ -50,6 +46,12 @@ export const useSwapOrderAmounts = ({
setState: Dispatch<Partial<SwapState>>;
}) => {
const currentMarket = useRootStore((state) => state.currentMarket);
const currentMarketData = useRootStore((store) => store.currentMarketData);
const resolvedFlashLoanFeeBps = useFlashLoanFeeBps({
provider: state.provider,
swapType: state.swapType,
marketData: currentMarketData,
});

useEffect(() => {
if (
Expand Down Expand Up @@ -96,7 +98,7 @@ export const useSwapOrderAmounts = ({
// const partnerFeeToken = state.side === 'sell' ? state.destinationToken : state.sourceToken;

const flashLoanFeeBps = isPositionSwap(state.swapType, state.useFlashloan ?? false)
? getFlashLoanFeeBps(state.provider)
? resolvedFlashLoanFeeBps
: 0;
const flashLoanFeeAmount =
state.side == 'sell'
Expand Down Expand Up @@ -354,6 +356,7 @@ export const useSwapOrderAmounts = ({
networkFeeAmountInBuyFormatted,
partnerFeeAmountFormatted: partnerFeeAmount.toFixed(),
flashLoanFeeAmountFormatted: flashLoanFeeAmount.toFixed(),
flashLoanFeeBps,
partnerFeeBps: partnetFeeBps,
});
}, [
Expand All @@ -366,5 +369,6 @@ export const useSwapOrderAmounts = ({
state.swapType,
state.orderType,
state.useFlashloan,
resolvedFlashLoanFeeBps,
]);
};
3 changes: 3 additions & 0 deletions src/components/transactions/Swap/types/state.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ export type TokensSwapState = {
partnerFeeAmountFormatted?: string;
/** Flash loan fee amount applied to this order, normalized to the fee token units (depends on side). */
flashLoanFeeAmountFormatted?: string;
/** Flash loan fee in basis points used to compute flashLoanFeeAmountFormatted. Resolved on-chain from ACLManager.isFlashBorrower. */
flashLoanFeeBps?: number;
/** Partner fee in basis points used to compute partnerFeeAmountFormatted. */
partnerFeeBps?: number;

Expand Down Expand Up @@ -287,6 +289,7 @@ export const swapDefaultState: SwapState = {
networkFeeAmountInBuyFormatted: '0',
partnerFeeAmountFormatted: '0',
flashLoanFeeAmountFormatted: '0',
flashLoanFeeBps: 0,
partnerFeeBps: 0,
limitsOrderButtonBlocked: false,
showSlippageWarning: false,
Expand Down
Loading