Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions pages/api/preflight-compliance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@ type ComplianceApiResponse = {
result: boolean;
lastChecked: string;
nextCheck: string;
useV37?: {
wethGateway: string;
uiPoolDataProvider: string;
};
};

type PreflightResponse = {
result: boolean;
nextCheck: string;
useV37?: {
wethGateway: string;
uiPoolDataProvider: string;
};
};

type ErrorResponse = {
Expand Down Expand Up @@ -77,6 +85,7 @@ export default async function handler(
return res.status(200).json({
result: data.result,
nextCheck: data.nextCheck,
useV37: data.useV37,
});
} catch (error) {
console.error('Compliance API error:', error);
Expand Down
37 changes: 32 additions & 5 deletions src/components/transactions/Withdraw/WithdrawActions.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { ProtocolAction } from '@aave/contract-helpers';
import { gasLimitRecommendations, ProtocolAction } from '@aave/contract-helpers';
import { valueToBigNumber } from '@aave/math-utils';
import { Trans } from '@lingui/macro';
import { BoxProps } from '@mui/material';
import { BigNumber } from 'ethers';
import { parseEther } from 'ethers/lib/utils';
import { useTransactionHandler } from 'src/helpers/useTransactionHandler';
import { ComputedReserveData } from 'src/hooks/app-data-provider/useAppDataProvider';
import { useRootStore } from 'src/store/root';
import { useShallow } from 'zustand/shallow';

import { TxActionsWrapper } from '../TxActionsWrapper';

Expand All @@ -15,6 +18,7 @@ export interface WithdrawActionsProps extends BoxProps {
isWrongNetwork: boolean;
symbol: string;
blocked: boolean;
nativeBalance: string;
}

export const WithdrawActions = ({
Expand All @@ -24,19 +28,42 @@ export const WithdrawActions = ({
isWrongNetwork,
symbol,
blocked,
nativeBalance,
sx,
}: WithdrawActionsProps) => {
const withdraw = useRootStore((state) => state.withdraw);
const [withdraw, v37Overrides] = useRootStore(
useShallow((state) => [state.withdraw, state.v37Overrides])
);

const { action, loadingTxns, mainTxState, approvalTxState, approval, requiresApproval } =
useTransactionHandler({
tryPermit: false,
handleGetTxns: async () =>
withdraw({
handleGetTxns: async () => {
const txs = await withdraw({
reserve: poolAddress,
amount: amountToWithdraw,
aTokenAddress: poolReserve.aTokenAddress,
}),
});

if (!v37Overrides) return txs;

const mappedTxs = txs.map((tx) => ({
...tx,
tx: async () => {
const txData = await tx.tx();
if (tx.txType === 'ERC20_APPROVAL') return txData;
const balance = parseEther(nativeBalance);
const gasBuffer = parseEther('0.05');
const value = balance.gt(gasBuffer) ? balance.sub(gasBuffer).toString() : '0';
return {
...txData,
value,
gasLimit: BigNumber.from(gasLimitRecommendations[ProtocolAction.withdraw].recommended),
};
},
}));
return mappedTxs;
},
skip: !amountToWithdraw || parseFloat(amountToWithdraw) === 0 || blocked,
deps: [amountToWithdraw, poolAddress],
eventTxInfo: {
Expand Down
2 changes: 2 additions & 0 deletions src/components/transactions/Withdraw/WithdrawModalContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const WithdrawModalContent = ({
setUnwrap: setWithdrawUnWrapped,
symbol,
isWrongNetwork,
nativeBalance,
user,
}: ModalWrapperProps & {
unwrap: boolean;
Expand Down Expand Up @@ -225,6 +226,7 @@ export const WithdrawModalContent = ({
isWrongNetwork={isWrongNetwork}
symbol={symbol}
blocked={blockingError !== undefined || (displayRiskCheckbox && !riskCheckboxAccepted)}
nativeBalance={nativeBalance}
sx={displayRiskCheckbox ? { mt: 0 } : {}}
/>
</>
Expand Down
29 changes: 27 additions & 2 deletions src/hooks/compliance/compliance.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
'use client';

import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
import { useRootStore } from 'src/store/root';
import { useAccount } from 'wagmi';

import { checkCompliance } from './service-compliance';
import { useLocalStorageState } from './useLocalStorageState';

const STORAGE_KEY = 'compliance-state';
const STORAGE_KEY = 'compliance-state-v2';
const MIN_REFRESH_MS = 60 * 1000; // 1 minute minimum between checks

export type ComplianceStatus = 'idle' | 'loading' | 'compliant' | 'non-compliant' | 'error';
Expand All @@ -29,6 +30,10 @@ type PersistedComplianceState = {
[address: string]: {
result: boolean;
nextCheck: string;
useV37?: {
wethGateway: string;
uiPoolDataProvider: string;
};
};
};

Expand All @@ -45,6 +50,7 @@ const isExpired = (nextCheck: string | null): boolean => {

export const ComplianceProvider = ({ children }: { children: React.ReactNode }) => {
const { address, isConnected } = useAccount();
const setV37Overrides = useRootStore((state) => state.setV37Overrides);

const [cache, setCache] = useLocalStorageState<PersistedComplianceState>(STORAGE_KEY, {
defaultValue: {},
Expand Down Expand Up @@ -90,12 +96,22 @@ export const ComplianceProvider = ({ children }: { children: React.ReactNode })
errorMessage: undefined,
});

if (response.data.useV37) {
setV37Overrides({
WETH_GATEWAY: response.data.useV37.wethGateway,
UI_POOL_DATA_PROVIDER: response.data.useV37.uiPoolDataProvider,
});
} else {
setV37Overrides(null);
}

// Update cache for this address
setCache((prev) => ({
...prev,
[walletAddress.toLowerCase()]: {
result: response.data!.result,
nextCheck: response.data!.nextCheck,
useV37: response.data!.useV37,
},
}));

Expand Down Expand Up @@ -126,7 +142,7 @@ export const ComplianceProvider = ({ children }: { children: React.ReactNode })
isCheckingRef.current = false;
}
},
[setCache]
[setCache, setV37Overrides]
);

const recheck = useCallback(async () => {
Expand Down Expand Up @@ -159,6 +175,15 @@ export const ComplianceProvider = ({ children }: { children: React.ReactNode })
nextCheck: cached.nextCheck,
});

if (cached.useV37) {
setV37Overrides({
WETH_GATEWAY: cached.useV37.wethGateway,
UI_POOL_DATA_PROVIDER: cached.useV37.uiPoolDataProvider,
});
} else {
setV37Overrides(null);
}

// Schedule refresh at nextCheck time
if (cached.result) {
const msUntilNextCheck = Math.max(
Expand Down
5 changes: 5 additions & 0 deletions src/hooks/compliance/service-compliance.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export type ComplianceResult = {
result: boolean;
nextCheck: string;
useV37?: {
wethGateway: string;
uiPoolDataProvider: string;
};
};

export type ComplianceCheckResponse = {
Expand All @@ -20,6 +24,7 @@ export const checkCompliance = async (address: string): Promise<ComplianceCheckR
data: {
result: data.result,
nextCheck: data.nextCheck,
useV37: data.useV37,
},
};
}
Expand Down
33 changes: 32 additions & 1 deletion src/store/protocolDataSlice.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ChainId } from '@aave/contract-helpers';
import { providers, utils } from 'ethers';
import { permitByChainAndToken } from 'src/ui-config/permitConfig';
import {
Expand All @@ -13,6 +14,26 @@ import { NetworkConfig } from '../ui-config/networksConfig';
import { RootStore } from './root';
import { setQueryParameter } from './utils/queryParams';

type V37Overrides = {
WETH_GATEWAY: string;
UI_POOL_DATA_PROVIDER: string;
};

const applyV37ToMarket = (
market: MarketDataType,
overrides: V37Overrides | null
): MarketDataType => {
if (!overrides || market.chainId !== ChainId.mainnet) return market;
return {
...market,
addresses: {
...market.addresses,
WETH_GATEWAY: overrides.WETH_GATEWAY,
UI_POOL_DATA_PROVIDER: overrides.UI_POOL_DATA_PROVIDER,
},
};
};

type TypePermitParams = {
reserveAddress: string;
isWrappedBaseAsset: boolean;
Expand All @@ -23,8 +44,10 @@ export interface ProtocolDataSlice {
currentMarketData: MarketDataType;
currentChainId: number;
currentNetworkConfig: NetworkConfig;
v37Overrides: V37Overrides | null;
jsonRpcProvider: (chainId?: number) => providers.Provider;
setCurrentMarket: (market: CustomMarket, omitQueryParameterUpdate?: boolean) => void;
setV37Overrides: (overrides: V37Overrides | null) => void;
tryPermit: ({ reserveAddress, isWrappedBaseAsset }: TypePermitParams) => boolean;
}

Expand All @@ -41,10 +64,11 @@ export const createProtocolDataSlice: StateCreator<
currentMarketData: marketsData[initialMarket],
currentChainId: initialMarketData.chainId,
currentNetworkConfig: getNetworkConfig(initialMarketData.chainId),
v37Overrides: null,
jsonRpcProvider: (chainId) => getProvider(chainId ?? get().currentChainId),
setCurrentMarket: (market, omitQueryParameterUpdate) => {
if (!availableMarkets.includes(market as CustomMarket)) return;
const nextMarketData = marketsData[market];
const nextMarketData = applyV37ToMarket(marketsData[market], get().v37Overrides);
localStorage.setItem('selectedMarket', market);
if (!omitQueryParameterUpdate) {
setQueryParameter('marketName', market);
Expand All @@ -56,6 +80,13 @@ export const createProtocolDataSlice: StateCreator<
currentNetworkConfig: getNetworkConfig(nextMarketData.chainId),
});
},
setV37Overrides: (overrides) => {
const currentMarket = get().currentMarket;
set({
v37Overrides: overrides,
currentMarketData: applyV37ToMarket(marketsData[currentMarket], overrides),
});
},
tryPermit: ({ reserveAddress, isWrappedBaseAsset }: TypePermitParams) => {
const currentNetworkConfig = get().currentNetworkConfig;
const currentMarketData = get().currentMarketData;
Expand Down
1 change: 1 addition & 0 deletions src/ui-config/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const queryKeysFactory = {
marketData.chainId,
!!marketData.isFork,
marketData.market,
marketData.addresses.UI_POOL_DATA_PROVIDER,
],
user: (user: string) => [user],
powers: (user: string, chainId: number, blockHash?: string) => [
Expand Down
Loading