diff --git a/src/components/incentives/IncentivesTooltipContent.tsx b/src/components/incentives/IncentivesTooltipContent.tsx index 3cbec0de25..8858231359 100644 --- a/src/components/incentives/IncentivesTooltipContent.tsx +++ b/src/components/incentives/IncentivesTooltipContent.tsx @@ -133,6 +133,11 @@ const IncentivesSymbolMap: { symbol: 'aEURC', aToken: true, }, + aEthUSDe: { + tokenIconSymbol: 'USDe', + symbol: 'aUSDe', + aToken: true, + }, }; interface IncentivesTooltipContentProps { diff --git a/src/components/incentives/MerklIncentivesTooltipContent.tsx b/src/components/incentives/MerklIncentivesTooltipContent.tsx index a590258bb2..29291fe865 100644 --- a/src/components/incentives/MerklIncentivesTooltipContent.tsx +++ b/src/components/incentives/MerklIncentivesTooltipContent.tsx @@ -50,21 +50,34 @@ export const MerklIncentivesTooltipContent = ({ - + Merkl rewards are claimed through the{' '} - + official app {'.'} + {merklIncentives.customClaimMessage ? ( + <> + {' '} + {merklIncentives.customClaimMessage} + + ) : null} + {merklIncentives.customMessage ? ( - {merklIncentives.customMessage} + {merklIncentives.customMessage}{' '} + + Learn more + ) : null} diff --git a/src/components/transactions/Swap/SwapModal.tsx b/src/components/transactions/Swap/SwapModal.tsx index ee9ec158b8..600a0ee16e 100644 --- a/src/components/transactions/Swap/SwapModal.tsx +++ b/src/components/transactions/Swap/SwapModal.tsx @@ -1,8 +1,11 @@ import { Trans } from '@lingui/macro'; +import { Box, Typography } from '@mui/material'; import React from 'react'; import { BasicModal } from 'src/components/primitives/BasicModal'; import { UserAuthenticated } from 'src/components/UserAuthenticated'; +import { ConnectWalletButton } from 'src/components/WalletConnection/ConnectWalletButton'; import { ModalContextType, ModalType, useModalContext } from 'src/hooks/useModal'; +import { useRootStore } from 'src/store/root'; import { ModalWrapper } from '../FlowCommons/ModalWrapper'; import { SwapModalContent } from './SwapModalContent'; @@ -11,15 +14,26 @@ export const SwapModal = () => { const { type, close, args } = useModalContext() as ModalContextType<{ underlyingAsset: string; }>; + const account = useRootStore((store) => store.account); + return ( - Swap} underlyingAsset={args.underlyingAsset}> - {(params) => ( - - {(user) => } - - )} - + {!account ? ( + + + Please connect your wallet to swap tokens. + + close()} /> + + ) : ( + Swap} underlyingAsset={args.underlyingAsset}> + {(params) => ( + + {(user) => } + + )} + + )} ); }; diff --git a/src/hooks/useEthenaIncentives.ts b/src/hooks/useEthenaIncentives.ts index cbdf16cd6c..fb6ebc496d 100644 --- a/src/hooks/useEthenaIncentives.ts +++ b/src/hooks/useEthenaIncentives.ts @@ -4,7 +4,7 @@ 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.USDe.A_TOKEN, 5], [AaveV3Ethereum.ASSETS.sUSDe.A_TOKEN, 5], [AaveV3EthereumLido.ASSETS.sUSDe.A_TOKEN, 5], [AaveV3Ethereum.ASSETS.GHO.V_TOKEN, 5], diff --git a/src/hooks/useMeritIncentives.ts b/src/hooks/useMeritIncentives.ts index 6d513b5e6d..00a0fd276f 100644 --- a/src/hooks/useMeritIncentives.ts +++ b/src/hooks/useMeritIncentives.ts @@ -48,6 +48,7 @@ export enum MeritAction { AVALANCHE_SUPPLY_USDT = 'avalanche-supply-usdt', AVALANCHE_SUPPLY_SAVAX = 'avalanche-supply-savax', AVALANCHE_SUPPLY_AUSD = 'avalanche-supply-ausd', + AVALANCHE_SUPPLY_GHO = 'avalanche-supply-gho', SONIC_SUPPLY_USDCE = 'sonic-supply-usdce', SONIC_SUPPLY_STS_BORROW_WS = 'sonic-supply-sts-borrow-ws', GNOSIS_BORROW_EURE = 'gnosis-borrow-eure', @@ -55,6 +56,7 @@ export enum MeritAction { CELO_SUPPLY_USDT = 'celo-supply-usdt', CELO_SUPPLY_USDC = 'celo-supply-usdc', CELO_SUPPLY_WETH = 'celo-supply-weth', + CELO_SUPPLY_MULTIPLE_BORROW_USDT = 'celo-supply-multiple-borrow-usdt', CELO_BORROW_CELO = 'celo-borrow-celo', CELO_BORROW_USDT = 'celo-borrow-usdt', CELO_BORROW_USDC = 'celo-borrow-usdc', @@ -110,6 +112,9 @@ const baseIncentivesWstETHCampaignsMessage = const baseIncentivesETHCampaignsMessage = 'Supplying ETH alone earns 1.25%, supplying ETH and borrowing USDC or EURC earns 1.50%, supplying ETH and borrowing GHO earns 1.75%. Some assets holding or positions on other protocols may impact the amount of rewards you are eligible for. Please check the forum post for the full eligibility criteria.'; +const celoSupplyMultipleBorrowUsdtMessage = + 'You must supply (CELO or ETH) and borrow USDT, in order to receive merit rewards. 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'; @@ -516,6 +521,15 @@ const MERIT_DATA_MAP: Record customForumLink: AusdRenewalForumLink, }, ], + GHO: [ + { + action: MeritAction.AVALANCHE_SUPPLY_GHO, + rewardTokenAddress: AaveV3Avalanche.ASSETS.GHO.A_TOKEN, + rewardTokenSymbol: 'aAvaSAVAX', + protocolAction: ProtocolAction.supply, + customMessage: antiLoopMessage, + }, + ], }, [CustomMarket.proto_sonic_v3]: { ['USDC']: [ @@ -572,6 +586,13 @@ const MERIT_DATA_MAP: Record protocolAction: ProtocolAction.supply, customMessage: antiLoopMessage, }, + { + action: MeritAction.CELO_SUPPLY_MULTIPLE_BORROW_USDT, + rewardTokenAddress: AaveV3Celo.ASSETS.CELO.A_TOKEN, + rewardTokenSymbol: 'aCelCELO', + protocolAction: ProtocolAction.supply, + customMessage: celoSupplyMultipleBorrowUsdtMessage, + }, { action: MeritAction.CELO_BORROW_CELO, rewardTokenAddress: AaveV3Celo.ASSETS.CELO.A_TOKEN, @@ -595,6 +616,13 @@ const MERIT_DATA_MAP: Record protocolAction: ProtocolAction.borrow, customMessage: antiLoopBorrowMessage, }, + { + action: MeritAction.CELO_SUPPLY_MULTIPLE_BORROW_USDT, + rewardTokenAddress: AaveV3Celo.ASSETS.CELO.A_TOKEN, + rewardTokenSymbol: 'aCelCELO', + protocolAction: ProtocolAction.borrow, + customMessage: celoSupplyMultipleBorrowUsdtMessage, + }, ], USDC: [ { @@ -620,6 +648,13 @@ const MERIT_DATA_MAP: Record protocolAction: ProtocolAction.supply, customMessage: antiLoopMessage, }, + { + action: MeritAction.CELO_SUPPLY_MULTIPLE_BORROW_USDT, + rewardTokenAddress: AaveV3Celo.ASSETS.CELO.A_TOKEN, + rewardTokenSymbol: 'aCelCELO', + protocolAction: ProtocolAction.supply, + customMessage: celoSupplyMultipleBorrowUsdtMessage, + }, { action: MeritAction.CELO_BORROW_WETH, rewardTokenAddress: AaveV3Celo.ASSETS.CELO.A_TOKEN, @@ -644,6 +679,7 @@ export const useMeritIncentives = ({ queryFn: async () => { const response = await fetch(url); const data = await response.json(); + const meritIncentives = data.currentAPR as MeritIncentives; return meritIncentives; @@ -655,27 +691,37 @@ export const useMeritIncentives = ({ if (!meritReserveIncentiveData) { return null; } - const incentive = meritReserveIncentiveData.find( + + const incentives = meritReserveIncentiveData.filter( (item) => item.protocolAction === protocolAction ); - if (!incentive) { + if (incentives.length === 0) { return null; } - const APR = data.actionsAPR[incentive.action]; + let maxAPR = null; + let selectedIncentive = null; + + for (const incentive of incentives) { + const APR = data.actionsAPR[incentive.action]; + if (APR && (maxAPR === null || APR > maxAPR)) { + maxAPR = APR; + selectedIncentive = incentive; + } + } - if (!APR) { + if (!selectedIncentive || maxAPR === null) { return null; } return { - incentiveAPR: (APR / 100).toString(), - rewardTokenAddress: incentive.rewardTokenAddress, - rewardTokenSymbol: incentive.rewardTokenSymbol, - action: incentive.action, - customMessage: incentive.customMessage, - customForumLink: incentive.customForumLink, + incentiveAPR: (maxAPR / 100).toString(), + rewardTokenAddress: selectedIncentive.rewardTokenAddress, + rewardTokenSymbol: selectedIncentive.rewardTokenSymbol, + action: selectedIncentive.action, + customMessage: selectedIncentive.customMessage, + customForumLink: selectedIncentive.customForumLink, } as ExtendedReserveIncentiveResponse; }, }); diff --git a/src/hooks/useMerklIncentives.ts b/src/hooks/useMerklIncentives.ts index d9e55939f3..3ca7e59543 100644 --- a/src/hooks/useMerklIncentives.ts +++ b/src/hooks/useMerklIncentives.ts @@ -87,11 +87,15 @@ type MerklOpportunity = { }; }; -export type ExtendedReserveIncentiveResponse = ReserveIncentiveResponse & { - customMessage: string; - customForumLink: string; +type ReserveIncentiveAdditionalData = { + customClaimMessage?: string; + customMessage?: string; + customForumLink?: string; }; +export type ExtendedReserveIncentiveResponse = ReserveIncentiveResponse & + ReserveIncentiveAdditionalData; + const allAaveAssets = [ AaveV3Ethereum.ASSETS, AaveV3EthereumLido.ASSETS, @@ -111,6 +115,28 @@ const allAaveAssets = [ AaveV3Soneium.ASSETS, ]; +const additionalIncentiveData: Record = { + [AaveV3Ethereum.ASSETS.USDe.A_TOKEN]: { + customMessage: + 'You must supply USDe and hold an equal or greater amount of sUSDe (by USD value) to receive the incentives. To be eligible, your assets supplied must be at least 2x your account equity, and you must not be borrowing any USDe.', + }, + [AaveV3Ethereum.ASSETS.USDtb.A_TOKEN]: { + customMessage: + 'You must supply USDtb to receive incentives. To be eligible, you must not be borrowing any USDtb.', + customClaimMessage: 'Rewards will be claimable starting in early August.', + customForumLink: 'https://x.com/ethena_labs/status/1950194502192550149', + }, +}; + +const hardcodedIncentives: Record = { + [AaveV3Ethereum.ASSETS.USDe.A_TOKEN]: { + incentiveAPR: '0.12', + rewardTokenAddress: AaveV3Ethereum.ASSETS.USDe.A_TOKEN, + rewardTokenSymbol: 'aEthUSDe', + ...additionalIncentiveData[AaveV3Ethereum.ASSETS.USDe.A_TOKEN], + }, +}; + const getUnderlyingAndAToken = (assets: { [key: string]: { UNDERLYING: Address; @@ -125,6 +151,7 @@ const getUnderlyingAndAToken = (assets: { const otherTokensWhitelisted = [ '0x04eadd7b10ea9a484c60860aea7a7c0aec09b9f0', // aUSDtb wrapper contract + '0x3a4de44B29995a3D8Cd02d46243E1563E55bCc8b', // Aave Ethereum USDe (wrapped) ]; const whitelistedRewardTokens = [ @@ -166,6 +193,12 @@ export const useMerklIncentives = ({ queryKey: ['merklIncentives', market], staleTime: 1000 * 60 * 5, select: (merklOpportunities) => { + const hardcodedIncentive = rewardedAsset ? hardcodedIncentives[rewardedAsset] : undefined; + + if (hardcodedIncentive) { + return hardcodedIncentive; + } + const opportunities = merklOpportunities.filter( (opportunitiy) => rewardedAsset && @@ -197,10 +230,15 @@ export const useMerklIncentives = ({ return null; } + const incentiveAdditionalData = rewardedAsset + ? additionalIncentiveData[rewardedAsset] + : undefined; + return { incentiveAPR: apr.toString(), rewardTokenAddress: rewardToken.address, rewardTokenSymbol: rewardToken.symbol, + ...incentiveAdditionalData, } as ExtendedReserveIncentiveResponse; }, }); diff --git a/src/layouts/MainLayout.tsx b/src/layouts/MainLayout.tsx index a8180995de..546da8339a 100644 --- a/src/layouts/MainLayout.tsx +++ b/src/layouts/MainLayout.tsx @@ -1,16 +1,24 @@ import { ChainId } from '@aave/contract-helpers'; import { Box } from '@mui/material'; +// import { useRouter } from 'next/router'; import React, { ReactNode } from 'react'; import AnalyticsConsent from 'src/components/Analytics/AnalyticsConsent'; import { useModalContext } from 'src/hooks/useModal'; import { FeedbackModal } from 'src/layouts/FeedbackDialog'; +// import { useRootStore } from 'src/store/root'; +// import { CustomMarket } from 'src/ui-config/marketsConfig'; import { FORK_ENABLED } from 'src/utils/marketsAndNetworksConfig'; +// import { useShallow } from 'zustand/shallow'; import { AppFooter } from './AppFooter'; import { AppHeader } from './AppHeader'; import TopBarNotify from './TopBarNotify'; -const getCampaignConfigs = (openSwitch: (token?: string, chainId?: number) => void) => ({ +const getCampaignConfigs = ( + // openSwitch: (token?: string, chainId?: number) => void, + openSwap: (underlyingAsset: string) => void + // openMarket: (market: CustomMarket) => void +) => ({ [ChainId.base]: { notifyText: 'A new incentives campaign is live on the Base market', buttonText: 'Explore Base', @@ -22,22 +30,107 @@ const getCampaignConfigs = (openSwitch: (token?: string, chainId?: number) => vo icon: '/icons/networks/base.svg', }, - [ChainId.sonic]: { - notifyText: 'Swaps are now live on Sonic', - buttonText: 'Swap Now', + // [ChainId.sonic]: { + // notifyText: 'Swaps are now live on Sonic', + // buttonText: 'Swap Now', + // buttonAction: { + // type: 'function' as const, + // value: () => openSwitch('', ChainId.sonic), + // }, + // bannerVersion: 'sonic-incentives-v1', + // icon: '/icons/networks/sonic.svg', + // }, + + [ChainId.mainnet]: { + notifyText: + 'If you hold Pendle PT tokens at maturity, you can now swap your collateral to a new maturity', + buttonText: 'Get Started', buttonAction: { type: 'function' as const, - value: () => openSwitch('', ChainId.sonic), + value: () => openSwap('0x3b3fb9c57858ef816833dc91565efcd85d96f634'), }, - bannerVersion: 'sonic-incentives-v1', - icon: '/icons/networks/sonic.svg', + bannerVersion: 'ethereum-swap-v1', + // icon: '/icons/networks/ethereum.svg', }, + + // [ChainId.polygon]: { + // notifyText: 'Swap tokens directly in the Aave App', + // buttonText: 'Swap Now', + // buttonAction: { + // type: 'function' as const, + // value: () => openSwitch('', ChainId.polygon), + // }, + // bannerVersion: 'polygon-swap-v1', + // icon: '/icons/networks/polygon.svg', + // }, + + // [ChainId.avalanche]: { + // notifyText: 'Swap tokens directly in the Aave App', + // buttonText: 'Swap Now', + // buttonAction: { + // type: 'function' as const, + // value: () => openSwitch('', ChainId.avalanche), + // }, + // bannerVersion: 'avalanche-swap-v1', + // icon: '/icons/networks/avalanche.svg', + // }, + + // [ChainId.arbitrum_one]: { + // notifyText: 'Swap tokens directly in the Aave App', + // buttonText: 'Swap Now', + // buttonAction: { + // type: 'function' as const, + // value: () => openSwitch('', ChainId.arbitrum_one), + // }, + // bannerVersion: 'arbitrum-swap-v1', + // icon: '/icons/networks/arbitrum.svg', + // }, + + // [ChainId.optimism]: { + // notifyText: 'Swap tokens directly in the Aave App', + // buttonText: 'Swap Now', + // buttonAction: { + // type: 'function' as const, + // value: () => openSwitch('', ChainId.optimism), + // }, + // bannerVersion: 'optimism-swap-v1', + // icon: '/icons/networks/optimism.svg', + // }, + + // [ChainId.xdai]: { + // notifyText: 'Swap tokens directly in the Aave App', + // buttonText: 'Swap Now', + // buttonAction: { + // type: 'function' as const, + // value: () => openSwitch('', ChainId.xdai), + // }, + // bannerVersion: 'gnosis-swap-v1', + // icon: '/icons/networks/gnosis.svg', + // }, + + // [ChainId.bnb]: { + // notifyText: 'Swap tokens directly in the Aave App', + // buttonText: 'Swap Now', + // buttonAction: { + // type: 'function' as const, + // value: () => openSwitch('', ChainId.bnb), + // }, + // bannerVersion: 'binance-swap-v1', + // icon: '/icons/networks/binance.svg', + // }, }); export function MainLayout({ children }: { children: ReactNode }) { - const { openSwitch } = useModalContext(); + const { openSwap } = useModalContext(); + // const router = useRouter(); + // const setCurrentMarket = useRootStore(useShallow((store) => store.setCurrentMarket)); + + // const openMarket = (market: CustomMarket) => { + // setCurrentMarket(market); + // router.push(`/markets/?marketName=${market}`); + // }; - const campaignConfigs = getCampaignConfigs(openSwitch); + const campaignConfigs = getCampaignConfigs(openSwap); return ( <> diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 5f875b640d..375becd103 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -1064,6 +1064,7 @@ msgstr "Reload" msgid "sGHO" msgstr "sGHO" +#: src/components/transactions/Swap/SwapModal.tsx #: src/components/transactions/Switch/BaseSwitchModal.tsx #: src/components/transactions/Switch/BaseSwitchModalContent.tsx msgid "Please connect your wallet to swap tokens." @@ -1128,6 +1129,7 @@ msgstr "deposited" #: src/components/incentives/MeritIncentivesTooltipContent.tsx #: src/components/incentives/MerklIncentivesTooltipContent.tsx +#: src/components/incentives/MerklIncentivesTooltipContent.tsx #: src/components/MarketSwitcher.tsx #: src/components/transactions/DelegationTxsWrapper.tsx #: src/components/transactions/DelegationTxsWrapper.tsx diff --git a/src/ui-config/reservePatches.ts b/src/ui-config/reservePatches.ts index 07cff6702a..7e8f5e7f83 100644 --- a/src/ui-config/reservePatches.ts +++ b/src/ui-config/reservePatches.ts @@ -165,6 +165,11 @@ export function fetchIconSymbolAndName({ underlyingAsset, symbol, name }: IconSy name: 'PT eUSDe August', iconSymbol: 'pteusde', }, + '0xbc6736d346a5ebc0debc997397912cd9b8fae10a': { + symbol: 'PT USDe September 25th 2025', + name: 'PT USDe September 2025', + iconSymbol: 'ptusde', + }, '0xa693B19d2931d498c5B318dF961919BB4aee87a5': { iconSymbol: 'UST', name: 'UST (Wormhole)' }, '0x59a19d8c652fa0284f44113d0ff9aba70bd46fb4': { iconSymbol: 'BPT_BAL_WETH' }, '0x1eff8af5d577060ba4ac8a29a13525bb0ee2a3d5': { iconSymbol: 'BPT_WBTC_WETH' },