diff --git a/.github/workflows/crowdin-download.yml b/.github/workflows/crowdin-download.yml
index d1187147a5..dd42b77687 100644
--- a/.github/workflows/crowdin-download.yml
+++ b/.github/workflows/crowdin-download.yml
@@ -13,6 +13,7 @@ jobs:
download-from-crowdin:
name: Download sources from Crowdin
runs-on: ubuntu-latest
+ if: github.repository == 'aave/interface'
steps:
- name: Checkout
diff --git a/package.json b/package.json
index 7f5aac95e3..bb56609835 100644
--- a/package.json
+++ b/package.json
@@ -31,8 +31,8 @@
"test:coverage": "jest --coverage"
},
"dependencies": {
- "@aave/contract-helpers": "1.30.5",
- "@aave/math-utils": "1.30.5",
+ "@aave/contract-helpers": "1.32.1",
+ "@aave/math-utils": "1.32.1",
"@bgd-labs/aave-address-book": "4.10.0",
"@emotion/cache": "11.10.3",
"@emotion/react": "11.10.4",
diff --git a/public/icons/other/ethena.svg b/public/icons/other/ethena.svg
new file mode 100644
index 0000000000..d16afab082
--- /dev/null
+++ b/public/icons/other/ethena.svg
@@ -0,0 +1 @@
+
diff --git a/public/icons/tokens/rez.svg b/public/icons/tokens/rez.svg
new file mode 100644
index 0000000000..820ff2fe74
--- /dev/null
+++ b/public/icons/tokens/rez.svg
@@ -0,0 +1 @@
+
diff --git a/src/components/UserDisplay.tsx b/src/components/UserDisplay.tsx
index a2273a27c1..efe15fc752 100644
--- a/src/components/UserDisplay.tsx
+++ b/src/components/UserDisplay.tsx
@@ -42,6 +42,7 @@ export const UserDisplay: React.FC = ({
}
invisibleBadge={!readOnlyMode}
diff --git a/src/components/incentives/EthenaIncentivesTooltipContent.tsx b/src/components/incentives/EthenaIncentivesTooltipContent.tsx
new file mode 100644
index 0000000000..ea9aa346c6
--- /dev/null
+++ b/src/components/incentives/EthenaIncentivesTooltipContent.tsx
@@ -0,0 +1,29 @@
+import { Trans } from '@lingui/macro';
+import { Box } from '@mui/material';
+
+import { Link } from '../primitives/Link';
+
+export const EthenaAirdropTooltipContent = ({ points }: { points: number }) => {
+ return (
+
+ {`This asset is eligible for ${({points}x)} Ethena Rewards.\n`}
+
+ {'Learn more about Ethena Rewards program'}{' '}
+
+ {'here'}
+
+ {'.'}
+
+
+
+ {`Aave Labs does not
+ guarantee the program and accepts no liability.\n`}
+
+
+ );
+};
diff --git a/src/components/incentives/IncentivesButton.tsx b/src/components/incentives/IncentivesButton.tsx
index 71c163ef62..4f8149ea7e 100644
--- a/src/components/incentives/IncentivesButton.tsx
+++ b/src/components/incentives/IncentivesButton.tsx
@@ -4,6 +4,7 @@ import { ReserveIncentiveResponse } from '@aave/math-utils/dist/esm/formatters/i
import { DotsHorizontalIcon } from '@heroicons/react/solid';
import { Box, SvgIcon, Typography } from '@mui/material';
import { useState } from 'react';
+import { useEthenaIncentives } from 'src/hooks/useEthenaIncentives';
import { useMeritIncentives } from 'src/hooks/useMeritIncentives';
import { useZkSyncIgniteIncentives } from 'src/hooks/useZkSyncIgniteIncentives';
import { useRootStore } from 'src/store/root';
@@ -12,6 +13,7 @@ import { DASHBOARD } from 'src/utils/mixPanelEvents';
import { ContentWithTooltip } from '../ContentWithTooltip';
import { FormattedNumber } from '../primitives/FormattedNumber';
import { TokenIcon } from '../primitives/TokenIcon';
+import { EthenaAirdropTooltipContent } from './EthenaIncentivesTooltipContent';
import { getSymbolMap, IncentivesTooltipContent } from './IncentivesTooltipContent';
import { MeritIncentivesTooltipContent } from './MeritIncentivesTooltipContent';
import { ZkSyncIgniteIncentivesTooltipContent } from './ZkSyncIgniteIncentivesTooltipContent';
@@ -92,6 +94,26 @@ export const ZkIgniteIncentivesButton = (params: {
);
};
+export const EthenaIncentivesButton = ({ rewardedAsset }: { rewardedAsset?: string }) => {
+ const [open, setOpen] = useState(false);
+ const points = useEthenaIncentives(rewardedAsset);
+
+ if (!points) {
+ return null;
+ }
+
+ return (
+ }
+ withoutHover
+ setOpen={setOpen}
+ open={open}
+ >
+
+
+ );
+};
+
export const IncentivesButton = ({ incentives, symbol, displayBlank }: IncentivesButtonProps) => {
const [open, setOpen] = useState(false);
@@ -271,3 +293,41 @@ const Content = ({
);
};
+
+const ContentEthenaButton = ({ points }: { points: number }) => {
+ const [open, setOpen] = useState(false);
+ const trackEvent = useRootStore((store) => store.trackEvent);
+
+ return (
+ ({
+ p: { xs: '0 4px', xsm: '2px 4px' },
+ border: `1px solid ${open ? theme.palette.action.disabled : theme.palette.divider}`,
+ borderRadius: '4px',
+ cursor: 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ transition: 'opacity 0.2s ease',
+ bgcolor: open ? 'action.hover' : 'transparent',
+ '&:hover': {
+ bgcolor: 'action.hover',
+ borderColor: 'action.disabled',
+ },
+ })}
+ onClick={() => {
+ trackEvent(DASHBOARD.VIEW_LM_DETAILS_DASHBOARD, {});
+ setOpen(!open);
+ }}
+ >
+
+
+ {`${points}x`}
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/incentives/IncentivesCard.tsx b/src/components/incentives/IncentivesCard.tsx
index d6bda0c6af..2129d2a6bf 100644
--- a/src/components/incentives/IncentivesCard.tsx
+++ b/src/components/incentives/IncentivesCard.tsx
@@ -6,6 +6,7 @@ import { ReactNode } from 'react';
import { FormattedNumber } from '../primitives/FormattedNumber';
import { NoData } from '../primitives/NoData';
import {
+ EthenaIncentivesButton,
IncentivesButton,
MeritIncentivesButton,
ZkIgniteIncentivesButton,
@@ -85,6 +86,7 @@ export const IncentivesCard = ({
rewardedAsset={address}
protocolAction={protocolAction}
/>
+
);
diff --git a/src/components/incentives/IncentivesTooltipContent.tsx b/src/components/incentives/IncentivesTooltipContent.tsx
index 3a46bcc9d7..738a2aa65f 100644
--- a/src/components/incentives/IncentivesTooltipContent.tsx
+++ b/src/components/incentives/IncentivesTooltipContent.tsx
@@ -48,6 +48,21 @@ const IncentivesSymbolMap: {
symbol: 'aPYUSD',
aToken: true,
},
+ aArbWETH: {
+ tokenIconSymbol: 'WETH',
+ symbol: 'aWETH',
+ aToken: true,
+ },
+ aArbwstETH: {
+ tokenIconSymbol: 'wstETH',
+ symbol: 'awstETH',
+ aToken: true,
+ },
+ aBaswstETH: {
+ tokenIconSymbol: 'wstETH',
+ symbol: 'awstETH',
+ aToken: true,
+ },
aAvaSAVAX: {
tokenIconSymbol: 'sAVAX',
symbol: 'asAVAX',
diff --git a/src/hooks/app-data-provider/useAppDataProvider.tsx b/src/hooks/app-data-provider/useAppDataProvider.tsx
index d9b613ceea..8f7bd65206 100644
--- a/src/hooks/app-data-provider/useAppDataProvider.tsx
+++ b/src/hooks/app-data-provider/useAppDataProvider.tsx
@@ -1,10 +1,12 @@
import {
+ ComputedUserReserve,
FormattedGhoReserveData,
FormattedGhoUserData,
formatUserSummaryWithDiscount,
USD_DECIMALS,
UserReserveData,
} from '@aave/math-utils';
+import { AaveV3Ethereum } from '@bgd-labs/aave-address-book';
import { formatUnits } from 'ethers/lib/utils';
import React, { PropsWithChildren, useContext } from 'react';
import { EmodeCategory } from 'src/helpers/types';
@@ -140,9 +142,42 @@ export const AppDataProvider: React.FC = ({ children }) => {
formatUnits(baseCurrencyData.marketReferenceCurrencyPriceInUsd, USD_DECIMALS)
),
});
+
+ const userGhoReserve = user.userReservesData.find(
+ (r) => r.reserve.underlyingAsset === AaveV3Ethereum.ASSETS.GHO.UNDERLYING.toLowerCase()
+ );
+
+ if (!userGhoReserve) {
+ throw new Error('GHO reserve not found in user reserves data');
+ }
+
+ const mergeUserReserves = (reserve: ComputedUserReserve) => {
+ if (reserve.underlyingAsset !== AaveV3Ethereum.ASSETS.GHO.UNDERLYING.toLowerCase()) {
+ return reserve;
+ }
+
+ if (reserve.variableBorrows === '0') {
+ return reserve;
+ }
+
+ // This amount takes into account the discount applied on the accrued interest.
+ const userGhoDebtBalance = formattedGhoUserData.userGhoBorrowBalance.toString();
+
+ // Merge with the user reserves so the correct debt balance can be shown throughout the app.
+ return {
+ ...reserve,
+ variableBorrows: userGhoDebtBalance,
+ variableBorrowsUSD: userGhoDebtBalance,
+ totalBorrowsUSD: userGhoDebtBalance,
+ totalBorrows: userGhoDebtBalance,
+ totalBorrowsMarketReferenceCurrency: userGhoDebtBalance,
+ };
+ };
+
user = {
...user,
...userSummaryWithDiscount,
+ userReservesData: user.userReservesData.map(mergeUserReserves),
};
}
}
diff --git a/src/hooks/pool/useUserYield.ts b/src/hooks/pool/useUserYield.ts
index 6cc2e91d1d..e8fb7c3170 100644
--- a/src/hooks/pool/useUserYield.ts
+++ b/src/hooks/pool/useUserYield.ts
@@ -155,7 +155,7 @@ export const useUserYields = (
) => {
return formatUserYield(formattedPoolReserves, undefined, undefined, user, marketData.market);
};
- if (GHO_MINTING_MARKETS.includes(marketData.marketTitle))
+ if (GHO_MINTING_MARKETS.includes(marketData.market))
return combineQueries(
[
elem,
diff --git a/src/hooks/useEthenaIncentives.ts b/src/hooks/useEthenaIncentives.ts
new file mode 100644
index 0000000000..e431a68264
--- /dev/null
+++ b/src/hooks/useEthenaIncentives.ts
@@ -0,0 +1,18 @@
+import { AaveV3Ethereum, AaveV3EthereumLido } from '@bgd-labs/aave-address-book';
+
+const getEthenaData = (assetAddress: string): number | undefined =>
+ ETHENA_DATA_MAP.get(assetAddress);
+
+const ETHENA_DATA_MAP: Map = new Map([
+ [AaveV3Ethereum.ASSETS.USDe.A_TOKEN, 25],
+ [AaveV3Ethereum.ASSETS.sUSDe.A_TOKEN, 5],
+ [AaveV3EthereumLido.ASSETS.sUSDe.A_TOKEN, 5],
+]);
+
+export const useEthenaIncentives = (rewardedAsset?: string) => {
+ if (!rewardedAsset) {
+ return undefined;
+ }
+
+ return getEthenaData(rewardedAsset);
+};
diff --git a/src/hooks/useMeritIncentives.ts b/src/hooks/useMeritIncentives.ts
index 1f28f6ed50..01ddbd6b98 100644
--- a/src/hooks/useMeritIncentives.ts
+++ b/src/hooks/useMeritIncentives.ts
@@ -1,6 +1,12 @@
import { ProtocolAction } from '@aave/contract-helpers';
import { ReserveIncentiveResponse } from '@aave/math-utils/dist/esm/formatters/incentive/calculate-reserve-incentives';
-import { AaveV3Avalanche, AaveV3Base, AaveV3Ethereum } from '@bgd-labs/aave-address-book';
+import {
+ AaveV3Arbitrum,
+ AaveV3Avalanche,
+ AaveV3Base,
+ AaveV3Ethereum,
+ AaveV3EthereumLido,
+} from '@bgd-labs/aave-address-book';
import { useQuery } from '@tanstack/react-query';
import { CustomMarket } from 'src/ui-config/marketsConfig';
@@ -8,10 +14,17 @@ export enum MeritAction {
ETHEREUM_STKGHO = 'ethereum-stkgho',
ETHEREUM_SUPPLY_PYUSD = 'ethereum-supply-pyusd',
ETHEREUM_SUPPLY_ETHX = 'ethereum-supply-ethx',
+ ETHEREUM_PRIME_SUPPLY_ETH = 'ethereum-prime-supply-weth',
+ ETHEREUM_PRIME_SUPPLY_EZETH = 'ethereum-prime-supply-ezeth',
SUPPLY_CBBTC_BORROW_USDC = 'ethereum-supply-cbbtc-borrow-usdc',
SUPPLY_WBTC_BORROW_USDT = 'ethereum-supply-wbtc-borrow-usdt',
+ ARBITRUM_SUPPLY_ETH = 'arbitrum-supply-weth',
+ ARBITRUM_SUPPLY_WSTETH = 'arbitrum-supply-wsteth',
+ ARBITRUM_SUPPLY_EZETH = 'arbitrum-supply-ezeth',
BASE_SUPPLY_CBBTC = 'base-supply-cbbtc',
BASE_SUPPLY_USDC = 'base-supply-usdc',
+ BASE_SUPPLY_WSTETH = 'base-supply-wsteth',
+ BASE_SUPPLY_EZETH = 'base-supply-ezeth',
BASE_BORROW_USDC = 'base-borrow-usdc',
AVALANCHE_SUPPLY_BTCB = 'avalanche-supply-btcb',
AVALANCHE_SUPPLY_USDC = 'avalanche-supply-usdc',
@@ -44,6 +57,11 @@ export type MeritReserveIncentiveData = Omit
MERIT_DATA_MAP[market]?.[symbol];
+const antiLoopMessage =
+ 'Borrowing of some assets may impact the amount of rewards you are eligible for. Please check the forum post for the full eligibility criteria.';
+const joinedEthCorrelatedIncentiveForumLink =
+ 'https://governance.aave.com/t/arfc-set-aci-as-emission-manager-for-liquidity-mining-programs/17898/56';
+
const MERIT_DATA_MAP: Record> = {
[CustomMarket.proto_mainnet_v3]: {
GHO: [
@@ -99,8 +117,7 @@ const MERIT_DATA_MAP: Record
protocolAction: ProtocolAction.supply,
customForumLink:
'https://governance.aave.com/t/arfc-pyusd-reserve-configuration-update-incentive-campaign/19573',
- customMessage:
- 'Borrowing of some assets may impact the amount of rewards you are eligible for. Please check the forum post for the full eligibility criteria.',
+ customMessage: antiLoopMessage,
},
],
ETHx: [
@@ -112,6 +129,80 @@ const MERIT_DATA_MAP: Record
},
],
},
+ [CustomMarket.proto_lido_v3]: {
+ ETH: [
+ {
+ action: MeritAction.ETHEREUM_PRIME_SUPPLY_ETH,
+ rewardTokenAddress: AaveV3EthereumLido.ASSETS.WETH.A_TOKEN,
+ rewardTokenSymbol: 'aEthLidoWETH',
+ protocolAction: ProtocolAction.supply,
+ customMessage: antiLoopMessage,
+ customForumLink: joinedEthCorrelatedIncentiveForumLink,
+ },
+ ],
+ WETH: [
+ {
+ action: MeritAction.ETHEREUM_PRIME_SUPPLY_ETH,
+ rewardTokenAddress: AaveV3EthereumLido.ASSETS.WETH.A_TOKEN,
+ rewardTokenSymbol: 'aEthLidoWETH',
+ protocolAction: ProtocolAction.supply,
+ customMessage: antiLoopMessage,
+ customForumLink: joinedEthCorrelatedIncentiveForumLink,
+ },
+ ],
+ ezETH: [
+ {
+ action: MeritAction.ETHEREUM_PRIME_SUPPLY_EZETH,
+ rewardTokenAddress: '0x3B50805453023a91a8bf641e279401a0b23FA6F9', // Renzo (REZ)
+ rewardTokenSymbol: 'REZ',
+ protocolAction: ProtocolAction.supply,
+ customMessage: antiLoopMessage,
+ customForumLink: joinedEthCorrelatedIncentiveForumLink,
+ },
+ ],
+ },
+ [CustomMarket.proto_arbitrum_v3]: {
+ ETH: [
+ {
+ action: MeritAction.ARBITRUM_SUPPLY_ETH,
+ rewardTokenAddress: AaveV3Arbitrum.ASSETS.WETH.A_TOKEN,
+ rewardTokenSymbol: 'aArbWETH',
+ protocolAction: ProtocolAction.supply,
+ customMessage: antiLoopMessage,
+ customForumLink: joinedEthCorrelatedIncentiveForumLink,
+ },
+ ],
+ WETH: [
+ {
+ action: MeritAction.ARBITRUM_SUPPLY_ETH,
+ rewardTokenAddress: AaveV3Arbitrum.ASSETS.WETH.A_TOKEN,
+ rewardTokenSymbol: 'aArbWETH',
+ protocolAction: ProtocolAction.supply,
+ customMessage: antiLoopMessage,
+ customForumLink: joinedEthCorrelatedIncentiveForumLink,
+ },
+ ],
+ wstETH: [
+ {
+ action: MeritAction.ARBITRUM_SUPPLY_WSTETH,
+ rewardTokenAddress: AaveV3Ethereum.ASSETS.wstETH.UNDERLYING,
+ rewardTokenSymbol: 'aArbwstETH',
+ protocolAction: ProtocolAction.supply,
+ customMessage: antiLoopMessage,
+ customForumLink: joinedEthCorrelatedIncentiveForumLink,
+ },
+ ],
+ ezETH: [
+ {
+ action: MeritAction.ARBITRUM_SUPPLY_EZETH,
+ rewardTokenAddress: '0x3B50805453023a91a8bf641e279401a0b23FA6F9', // Renzo (REZ)
+ rewardTokenSymbol: 'REZ',
+ protocolAction: ProtocolAction.supply,
+ customMessage: antiLoopMessage,
+ customForumLink: joinedEthCorrelatedIncentiveForumLink,
+ },
+ ],
+ },
[CustomMarket.proto_base_v3]: {
cbBTC: [
{
@@ -135,6 +226,26 @@ const MERIT_DATA_MAP: Record
protocolAction: ProtocolAction.borrow,
},
],
+ wstETH: [
+ {
+ action: MeritAction.BASE_SUPPLY_WSTETH,
+ rewardTokenAddress: AaveV3Base.ASSETS.wstETH.UNDERLYING,
+ rewardTokenSymbol: 'aBaswstETH',
+ protocolAction: ProtocolAction.supply,
+ customMessage: antiLoopMessage,
+ customForumLink: joinedEthCorrelatedIncentiveForumLink,
+ },
+ ],
+ ezETH: [
+ {
+ action: MeritAction.BASE_SUPPLY_EZETH,
+ rewardTokenAddress: '0x3B50805453023a91a8bf641e279401a0b23FA6F9', // Renzo (REZ)
+ rewardTokenSymbol: 'REZ',
+ protocolAction: ProtocolAction.supply,
+ customMessage: antiLoopMessage,
+ customForumLink: joinedEthCorrelatedIncentiveForumLink,
+ },
+ ],
},
[CustomMarket.proto_avalanche_v3]: {
['BTC.b']: [
diff --git a/src/layouts/MainLayout.tsx b/src/layouts/MainLayout.tsx
index 7228d00326..6b3e8f94bf 100644
--- a/src/layouts/MainLayout.tsx
+++ b/src/layouts/MainLayout.tsx
@@ -2,23 +2,43 @@ import { Box } from '@mui/material';
import React, { ReactNode } from 'react';
import AnalyticsConsent from 'src/components/Analytics/AnalyticsConsent';
import { FeedbackModal } from 'src/layouts/FeedbackDialog';
+import { useRootStore } from 'src/store/root';
import { FORK_ENABLED } from 'src/utils/marketsAndNetworksConfig';
import { AppFooter } from './AppFooter';
import { AppHeader } from './AppHeader';
-// import TopBarNotify from './TopBarNotify';
+import TopBarNotify from './TopBarNotify';
+const SwitchIcon = () => (
+
+);
export function MainLayout({ children }: { children: ReactNode }) {
- // const APP_BANNER_VERSION = '5.0.0';
-
+ const APP_BANNER_VERSION = '6.0.0';
+ const currentMarket = useRootStore((state) => state.currentMarket);
return (
<>
- {/* */}
+ {currentMarket === 'proto_base_v3' && (
+ }
+ />
+ )}
+
{children}
diff --git a/src/layouts/TopBarNotify.tsx b/src/layouts/TopBarNotify.tsx
index e9322acc1e..6149460470 100644
--- a/src/layouts/TopBarNotify.tsx
+++ b/src/layouts/TopBarNotify.tsx
@@ -17,6 +17,7 @@ interface TopBarNotifyProps {
buttonText?: string;
bannerVersion: string;
icon?: string;
+ customIcon?: ReactNode;
}
export default function TopBarNotify({
@@ -25,6 +26,7 @@ export default function TopBarNotify({
buttonText,
bannerVersion,
icon,
+ customIcon,
}: TopBarNotifyProps) {
const { breakpoints } = useTheme();
const md = useMediaQuery(breakpoints.down('md'));
@@ -86,6 +88,8 @@ export default function TopBarNotify({
>
{notifyText}
+ {customIcon ? customIcon : null}
+
{icon && !sm ? : ''}
{learnMoreLink && md ? (
@@ -99,6 +103,7 @@ export default function TopBarNotify({
) : null}
+
{!md && learnMoreLink ? (