Skip to content

Commit 21b2292

Browse files
committed
feat: resolve flashloan fee on-chain via ACLManager
1 parent cf24dad commit 21b2292

8 files changed

Lines changed: 125 additions & 14 deletions

File tree

src/components/transactions/Swap/actions/CollateralSwap/CollateralSwapActionsViaCoWAdapters.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export const CollateralSwapActionsViaCowAdapters = ({
189189
: undefined;
190190

191191
const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
192-
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
192+
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
193193
sellAmount: state.sellAmountBigInt,
194194
});
195195

src/components/transactions/Swap/actions/DebtSwap/DebtSwapActionsViaCoW.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ export const DebtSwapActionsViaCoW = ({
215215
: undefined;
216216

217217
const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
218-
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
218+
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
219219
sellAmount: BigInt(sellAmountWithMarginForDustProtection),
220220
});
221221

src/components/transactions/Swap/actions/RepayWithCollateral/RepayWithCollateralActionsViaCoW.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ export const RepayWithCollateralActionsViaCoW = ({
213213
: undefined;
214214

215215
const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
216-
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
216+
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
217217
sellAmount: BigInt(sellAmountWithMarginForDustProtection),
218218
});
219219

src/components/transactions/Swap/helpers/cow/adapters.helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export const calculateInstanceAddress = async ({
9292
};
9393

9494
const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
95-
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
95+
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
9696
sellAmount: BigInt(sellAmountWithMarginForDustProtection),
9797
});
9898

@@ -180,7 +180,7 @@ export const calculateFlashLoanAmounts = (
180180

181181
const { flashLoanFeeAmount, sellAmountToSign } = flashLoanSdk.calculateFlashLoanAmounts({
182182
sellAmount: sellAmount,
183-
flashLoanFeeBps: FLASH_LOAN_FEE_BPS,
183+
flashLoanFeeBps: state.flashLoanFeeBps ?? FLASH_LOAN_FEE_BPS,
184184
});
185185

186186
return {

src/components/transactions/Swap/helpers/paraswap/flashloan.helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ export const calculateParaswapFlashLoanFee = (
3131

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

3737
// Format the fee amount
3838
const flashLoanFeeFormatted = valueToBigNumber(flashLoanFeeAmount.toString())
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {
2+
AaveV3Arbitrum,
3+
AaveV3Avalanche,
4+
AaveV3Base,
5+
AaveV3BNB,
6+
AaveV3Ethereum,
7+
AaveV3EthereumEtherFi,
8+
AaveV3EthereumLido,
9+
AaveV3Gnosis,
10+
AaveV3Optimism,
11+
AaveV3Polygon,
12+
AaveV3Sonic,
13+
} from '@aave-dao/aave-address-book';
14+
import { SupportedChainId } from '@cowprotocol/cow-sdk';
15+
import { useQuery } from '@tanstack/react-query';
16+
import { Contract } from 'ethers';
17+
import { CustomMarket, MarketDataType } from 'src/ui-config/marketsConfig';
18+
import { getProvider } from 'src/utils/marketsAndNetworksConfig';
19+
20+
import { ADAPTER_FACTORY, FLASH_LOAN_FEE_BPS } from '../constants/cow.constants';
21+
import { PARASWAP_FLASH_LOAN_FEE_BPS } from '../constants/paraswap.constants';
22+
import { SwapProvider, SwapType } from '../types';
23+
24+
const ACL_MANAGER_ABI = ['function isFlashBorrower(address) view returns (bool)'];
25+
26+
const ACL_MANAGER_BY_MARKET: Partial<Record<CustomMarket, string>> = {
27+
[CustomMarket.proto_mainnet_v3]: AaveV3Ethereum.ACL_MANAGER,
28+
[CustomMarket.proto_lido_v3]: AaveV3EthereumLido.ACL_MANAGER,
29+
[CustomMarket.proto_etherfi_v3]: AaveV3EthereumEtherFi.ACL_MANAGER,
30+
[CustomMarket.proto_arbitrum_v3]: AaveV3Arbitrum.ACL_MANAGER,
31+
[CustomMarket.proto_base_v3]: AaveV3Base.ACL_MANAGER,
32+
[CustomMarket.proto_polygon_v3]: AaveV3Polygon.ACL_MANAGER,
33+
[CustomMarket.proto_optimism_v3]: AaveV3Optimism.ACL_MANAGER,
34+
[CustomMarket.proto_avalanche_v3]: AaveV3Avalanche.ACL_MANAGER,
35+
[CustomMarket.proto_sonic_v3]: AaveV3Sonic.ACL_MANAGER,
36+
[CustomMarket.proto_gnosis_v3]: AaveV3Gnosis.ACL_MANAGER,
37+
[CustomMarket.proto_bnb_v3]: AaveV3BNB.ACL_MANAGER,
38+
};
39+
40+
const fallbackBps = (provider: SwapProvider): number =>
41+
provider === SwapProvider.COW_PROTOCOL ? FLASH_LOAN_FEE_BPS : PARASWAP_FLASH_LOAN_FEE_BPS;
42+
43+
const resolveTarget = (
44+
provider: SwapProvider,
45+
swapType: SwapType,
46+
marketData: MarketDataType
47+
): string | undefined => {
48+
if (provider === SwapProvider.COW_PROTOCOL) {
49+
return ADAPTER_FACTORY[marketData.chainId as unknown as SupportedChainId] || undefined;
50+
}
51+
if (provider === SwapProvider.PARASWAP) {
52+
switch (swapType) {
53+
case SwapType.CollateralSwap:
54+
return marketData.addresses.SWAP_COLLATERAL_ADAPTER;
55+
case SwapType.RepayWithCollateral:
56+
return marketData.addresses.REPAY_WITH_COLLATERAL_ADAPTER;
57+
case SwapType.DebtSwap:
58+
return marketData.addresses.DEBT_SWITCH_ADAPTER;
59+
case SwapType.WithdrawAndSwap:
60+
return marketData.addresses.WITHDRAW_SWITCH_ADAPTER;
61+
default:
62+
return undefined;
63+
}
64+
}
65+
return undefined;
66+
};
67+
68+
/**
69+
* Resolve the flashloan fee bps for the active swap, defaulting to the
70+
* provider's hardcoded bps when the on-chain ACLManager check is unavailable
71+
* or pending. Returns 0 only when the target adapter (Paraswap per-flow) /
72+
* factory (CoW) is registered as a FLASH_BORROWER on the market's ACLManager.
73+
*/
74+
export const useFlashLoanFeeBps = ({
75+
provider,
76+
swapType,
77+
marketData,
78+
}: {
79+
provider: SwapProvider;
80+
swapType: SwapType;
81+
marketData: MarketDataType;
82+
}): number => {
83+
const aclManager = ACL_MANAGER_BY_MARKET[marketData.market];
84+
const target = resolveTarget(provider, swapType, marketData);
85+
const fallback = fallbackBps(provider);
86+
87+
const enabled = Boolean(aclManager && target);
88+
89+
const { data: isWhitelisted } = useQuery({
90+
queryFn: async (): Promise<boolean> => {
91+
const contract = new Contract(
92+
aclManager as string,
93+
ACL_MANAGER_ABI,
94+
getProvider(marketData.chainId)
95+
);
96+
return contract.isFlashBorrower(target);
97+
},
98+
queryKey: ['flashBorrowerCheck', marketData.chainId, aclManager, target],
99+
enabled,
100+
staleTime: 5 * 60 * 1000,
101+
});
102+
103+
return isWhitelisted === true ? 0 : fallback;
104+
};

src/components/transactions/Swap/hooks/useSwapOrderAmounts.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import { OrderKind } from '@cowprotocol/cow-sdk';
33
import { Dispatch, useEffect } from 'react';
44
import { useRootStore } from 'src/store/root';
55

6-
import { COW_PARTNER_FEE, FLASH_LOAN_FEE_BPS } from '../constants/cow.constants';
7-
import { PARASWAP_FLASH_LOAN_FEE_BPS } from '../constants/paraswap.constants';
6+
import { COW_PARTNER_FEE } from '../constants/cow.constants';
87
import {
98
isCowProtocolRates,
109
OrderType,
@@ -13,6 +12,7 @@ import {
1312
SwapState,
1413
SwapType,
1514
} from '../types';
15+
import { useFlashLoanFeeBps } from './useFlashLoanFeeBps';
1616
import { swapTypesThatRequiresInvertedQuote } from './useSwapQuote';
1717

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

30-
const getFlashLoanFeeBps = (provider: SwapProvider) => {
31-
return provider === SwapProvider.COW_PROTOCOL ? FLASH_LOAN_FEE_BPS : PARASWAP_FLASH_LOAN_FEE_BPS;
32-
};
33-
3430
/**
3531
* Computes normalized sell/buy amounts used to build transactions.
3632
*
@@ -50,6 +46,12 @@ export const useSwapOrderAmounts = ({
5046
setState: Dispatch<Partial<SwapState>>;
5147
}) => {
5248
const currentMarket = useRootStore((state) => state.currentMarket);
49+
const currentMarketData = useRootStore((store) => store.currentMarketData);
50+
const resolvedFlashLoanFeeBps = useFlashLoanFeeBps({
51+
provider: state.provider,
52+
swapType: state.swapType,
53+
marketData: currentMarketData,
54+
});
5355

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

98100
const flashLoanFeeBps = isPositionSwap(state.swapType, state.useFlashloan ?? false)
99-
? getFlashLoanFeeBps(state.provider)
101+
? resolvedFlashLoanFeeBps
100102
: 0;
101103
const flashLoanFeeAmount =
102104
state.side == 'sell'
@@ -354,6 +356,7 @@ export const useSwapOrderAmounts = ({
354356
networkFeeAmountInBuyFormatted,
355357
partnerFeeAmountFormatted: partnerFeeAmount.toFixed(),
356358
flashLoanFeeAmountFormatted: flashLoanFeeAmount.toFixed(),
359+
flashLoanFeeBps,
357360
partnerFeeBps: partnetFeeBps,
358361
});
359362
}, [
@@ -366,5 +369,6 @@ export const useSwapOrderAmounts = ({
366369
state.swapType,
367370
state.orderType,
368371
state.useFlashloan,
372+
resolvedFlashLoanFeeBps,
369373
]);
370374
};

src/components/transactions/Swap/types/state.types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ export type TokensSwapState = {
9797
partnerFeeAmountFormatted?: string;
9898
/** Flash loan fee amount applied to this order, normalized to the fee token units (depends on side). */
9999
flashLoanFeeAmountFormatted?: string;
100+
/** Flash loan fee in basis points used to compute flashLoanFeeAmountFormatted. Resolved on-chain from ACLManager.isFlashBorrower. */
101+
flashLoanFeeBps?: number;
100102
/** Partner fee in basis points used to compute partnerFeeAmountFormatted. */
101103
partnerFeeBps?: number;
102104

@@ -287,6 +289,7 @@ export const swapDefaultState: SwapState = {
287289
networkFeeAmountInBuyFormatted: '0',
288290
partnerFeeAmountFormatted: '0',
289291
flashLoanFeeAmountFormatted: '0',
292+
flashLoanFeeBps: 0,
290293
partnerFeeBps: 0,
291294
limitsOrderButtonBlocked: false,
292295
showSlippageWarning: false,

0 commit comments

Comments
 (0)