Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ package-lock.json

# Sentry Config File
.env.sentry-build-plugin
tsconfig.tsbuildinfo
82 changes: 70 additions & 12 deletions src/components/incentives/IncentivesButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import { Box, Typography } from '@mui/material';
import { useState } from 'react';
import { useEthenaIncentives } from 'src/hooks/useEthenaIncentives';
import { useEtherfiIncentives } from 'src/hooks/useEtherfiIncentives';
import { useMeritIncentives } from 'src/hooks/useMeritIncentives';
import {
ExtendedReserveIncentiveResponse as MeritReserveIncentiveResponse,
MeritAction,
useMeritIncentives,
} from 'src/hooks/useMeritIncentives';
import { ExtendedReserveIncentiveResponse, useMerklIncentives } from 'src/hooks/useMerklIncentives';
import { useMerklPointsIncentives } from 'src/hooks/useMerklPointsIncentives';
import { useSavingsGhoIncentive } from 'src/hooks/useSavingsGhoIncentive';
import { useSonicIncentives } from 'src/hooks/useSonicIncentives';
import { useRootStore } from 'src/store/root';
import { DASHBOARD } from 'src/utils/events';
Expand Down Expand Up @@ -128,23 +133,23 @@ const BlankIncentives = () => {
);
};

export const MeritIncentivesButton = (params: {
type MeritIncentivesButtonParams = {
symbol: string;
market: string;
protocolAction?: ProtocolAction;
protocolAPY?: number;
protocolIncentives?: ReserveIncentiveResponse[];
hideValue?: boolean;
};

const MeritIncentivesButtonContent = ({
meritIncentives,
hideValue,
}: {
meritIncentives: MeritReserveIncentiveResponse;
hideValue?: boolean;
}) => {
const [open, setOpen] = useState(false);
const { data: meritIncentives } = useMeritIncentives(params);

if (!meritIncentives) {
return null;
}

// Show only merit incentives APR
const displayValue = +meritIncentives.incentiveAPR;

return (
<ContentWithTooltip
Expand All @@ -160,13 +165,66 @@ export const MeritIncentivesButton = (params: {
>
<Content
incentives={[meritIncentives]}
incentivesNetAPR={displayValue}
hideValue={params.hideValue}
incentivesNetAPR={+meritIncentives.incentiveAPR}
hideValue={hideValue}
/>
</ContentWithTooltip>
);
};

export const SavingsGhoIncentivesButton = ({ hideValue }: { hideValue?: boolean }) => {
const { data: savingsGhoIncentive } = useSavingsGhoIncentive();

if (!savingsGhoIncentive) {
return null;
}

const meritIncentivesAPY = convertAprToApy(parseFloat(savingsGhoIncentive.aprDecimal));
const action = savingsGhoIncentive.actionKey || MeritAction.ETHEREUM_SGHO;
const meritIncentives: MeritReserveIncentiveResponse = {
incentiveAPR: meritIncentivesAPY.toString(),
rewardTokenAddress: savingsGhoIncentive.rewardTokenAddress || '',
rewardTokenSymbol: savingsGhoIncentive.rewardTokenSymbol || 'sGHO',
activeActions: [action],
actionMessages: {
[action]: {
customMessage: savingsGhoIncentive.customMessage ?? undefined,
customForumLink: savingsGhoIncentive.customForumLink ?? undefined,
},
},
action,
customMessage: savingsGhoIncentive.customMessage ?? undefined,
customForumLink: savingsGhoIncentive.customForumLink ?? undefined,
variants: { selfAPY: null },
breakdown: {
protocolAPY: 0,
protocolIncentivesAPR: 0,
meritIncentivesAPR: meritIncentivesAPY,
totalAPY: meritIncentivesAPY,
isBorrow: false,
breakdown: {
protocol: 0,
protocolIncentives: 0,
meritIncentives: meritIncentivesAPY,
},
},
};

return <MeritIncentivesButtonContent meritIncentives={meritIncentives} hideValue={hideValue} />;
};

export const MeritIncentivesButton = (params: MeritIncentivesButtonParams) => {
const { data: meritIncentives } = useMeritIncentives(params);

if (!meritIncentives) {
return null;
}

return (
<MeritIncentivesButtonContent meritIncentives={meritIncentives} hideValue={params.hideValue} />
);
};

export const MerklIncentivesButton = (params: {
market: string;
rewardedAsset?: string;
Expand Down
1 change: 1 addition & 0 deletions src/components/incentives/IncentivesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface IncentivesCardProps {
symbol: string;
value: string | number;
incentives?: ReserveIncentiveResponse[];
/** aToken / vToken address (legacy; hook resolves underlying internally). */
address?: string;
variant?: 'main14' | 'main16' | 'secondary14';
symbolsVariant?: 'secondary14' | 'secondary16';
Expand Down
5 changes: 5 additions & 0 deletions src/components/incentives/IncentivesTooltipContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ const IncentivesSymbolMap: {
symbol: 'aUSDm',
aToken: true,
},
aCelUSDT: {
tokenIconSymbol: 'USDT',
symbol: 'aUSDT',
aToken: true,
},
aGnoEURe: {
tokenIconSymbol: 'EURe',
symbol: 'aEURe',
Expand Down
33 changes: 19 additions & 14 deletions src/components/incentives/MeritIncentivesTooltipContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,19 @@ interface CampaignConfig {
}

const isCeloAction = (action: MeritAction): boolean => {
return [
MeritAction.CELO_SUPPLY_CELO,
MeritAction.CELO_SUPPLY_USDT,
MeritAction.CELO_SUPPLY_USDC,
MeritAction.CELO_SUPPLY_WETH,
MeritAction.CELO_SUPPLY_MULTIPLE_BORROW_USDT,
MeritAction.CELO_BORROW_CELO,
MeritAction.CELO_BORROW_USDT,
MeritAction.CELO_BORROW_USDC,
MeritAction.CELO_BORROW_WETH,
].includes(action);
return (
[
MeritAction.CELO_SUPPLY_CELO,
MeritAction.CELO_SUPPLY_USDT,
MeritAction.CELO_SUPPLY_USDC,
MeritAction.CELO_SUPPLY_WETH,
MeritAction.CELO_SUPPLY_MULTIPLE_BORROW_USDT,
MeritAction.CELO_BORROW_CELO,
MeritAction.CELO_BORROW_USDT,
MeritAction.CELO_BORROW_USDC,
MeritAction.CELO_BORROW_WETH,
] as string[]
).includes(action);
};

const selfCampaignConfig: Map<MeritAction, { limit: string; token: string }> = new Map([
Expand Down Expand Up @@ -122,12 +124,15 @@ export const MeritIncentivesTooltipContent = ({
};
const meritIncentivesFormatted = getSymbolMap(meritIncentives);
const isCombinedMeritIncentives: boolean = meritIncentives.activeActions.length > 1;
const campaignConfig = getCampaignConfig(meritIncentives.action);
const selfConfig = selfCampaignConfig.get(meritIncentives.action);
// `action` is now optional (backend-driven). Fall back to an empty string
// so the switch/lookup helpers match their STANDARD branch.
const primaryAction = meritIncentives.action ?? '';
const campaignConfig = getCampaignConfig(primaryAction);
const selfConfig = selfCampaignConfig.get(primaryAction);

const remainingCustomMessage = getRemainingMessagesWhenCombined(
meritIncentives.activeActions,
meritIncentives.action,
primaryAction,
isCombinedMeritIncentives,
meritIncentives.actionMessages
);
Expand Down
52 changes: 4 additions & 48 deletions src/components/incentives/MerklIncentivesTooltipContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,55 +185,11 @@ export const MerklIncentivesTooltipContent = ({
</Typography>
</Box>
</Row>
) : merklIncentives.rewardsTokensMappedApys &&
merklIncentives.rewardsTokensMappedApys.length > 1 ? (
<>
{merklIncentives.rewardsTokensMappedApys.map((reward, index) => {
const { tokenIconSymbol, symbol, aToken } = getSymbolMap({
rewardTokenSymbol: reward.token.symbol,
rewardTokenAddress: reward.token.address,
incentiveAPR: reward.apy.toString(),
});
return (
<Row
key={index}
height={32}
caption={
<Box
sx={{
display: 'flex',
alignItems: 'center',
mb: 0,
}}
>
<TokenIcon
symbol={tokenIconSymbol}
aToken={aToken}
sx={{ fontSize: '20px', mr: 1 }}
/>
<Typography variant={typographyVariant}>{symbol}</Typography>
<Typography variant={typographyVariant} sx={{ ml: 0.5 }}>
{merklIncentives.breakdown.isBorrow ? '(-)' : '(+)'}
</Typography>
</Box>
}
width="100%"
>
<Box sx={{ display: 'inline-flex', alignItems: 'center' }}>
<FormattedNumber
value={merklIncentives.breakdown.isBorrow ? -reward.apy : reward.apy}
percent
variant={typographyVariant}
/>
<Typography variant={typographyVariant} sx={{ ml: 1 }}>
<Trans>APY</Trans>
</Typography>
</Box>
</Row>
);
})}
</>
) : (
// Note: legacy multi-reward-token rendering (`rewardsTokensMappedApys`)
// is gone. The V3 backend returns one `MerklSupply/Borrow`
// variant per reserve per direction with a single `payoutToken`,
// so the single-row render below covers all live campaigns.
<Row
height={32}
caption={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { Typography } from '@mui/material';
import React, { useRef, useState } from 'react';
import { useGeneralStakeUiData } from 'src/hooks/stake/useGeneralStakeUiData';
import { useUserStakeUiData } from 'src/hooks/stake/useUserStakeUiData';
import { useMeritIncentives } from 'src/hooks/useMeritIncentives';
import { useModalContext } from 'src/hooks/useModal';
import { useSavingsGhoIncentive } from 'src/hooks/useSavingsGhoIncentive';
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
import { useRootStore } from 'src/store/root';
import { stakeAssetNameFormatted, stakeConfig } from 'src/ui-config/stakeConfig';
import { STAKE } from 'src/utils/events';
import { GHO_SYMBOL } from 'src/utils/ghoUtilities';
import { convertAprToApy } from 'src/utils/utils';
import { useShallow } from 'zustand/shallow';

import { AssetInput } from '../AssetInput';
Expand All @@ -35,10 +36,10 @@ export const SavingsGhoModalDepositContent = () => {
store.currentChainId,
])
);
const { data: meritIncentives } = useMeritIncentives({
symbol: 'GHO',
market: currentMarketData.market,
});
const { data: savingsGhoIncentive } = useSavingsGhoIncentive();
const savingsGhoAPY = savingsGhoIncentive?.aprDecimal
? convertAprToApy(Number(savingsGhoIncentive.aprDecimal))
: 0;
const [_amount, setAmount] = useState('');
const amountRef = useRef<string>();

Expand Down Expand Up @@ -118,11 +119,7 @@ export const SavingsGhoModalDepositContent = () => {
</Typography>
)}
<TxModalDetails gasLimit={gasLimit} chainId={ChainId.mainnet}>
<DetailsNumberLine
description={<Trans>APR</Trans>}
value={meritIncentives?.incentiveAPR || '0'}
percent
/>
<DetailsNumberLine description={<Trans>APY</Trans>} value={savingsGhoAPY} percent />
</TxModalDetails>

{txError && <GasEstimationError txError={txError} />}
Expand Down
7 changes: 6 additions & 1 deletion src/hooks/app-data-provider/useAppDataProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,12 @@ export const AppDataProvider: React.FC<PropsWithChildren> = ({ children }) => {

const marketAddress = currentMarketData.addresses.LENDING_POOL.toLowerCase();

const sdkMarket = data?.find((item) => item.address.toLowerCase() === marketAddress);
// react-query's structural sharing can replace our Market[] with a
// structurally-similar plain object on refetch when it encounters
// non-POJO values (e.g. bigint-ish strings wrapped by the SDK). Guard
// before calling Array.prototype methods.
const marketsList = Array.isArray(data) ? data : [];
const sdkMarket = marketsList.find((item) => item.address.toLowerCase() === marketAddress);

const totalBorrows = sdkMarket?.borrowReserves.reduce((acc, reserve) => {
const value = reserve.borrowInfo?.total?.usd ?? 0;
Expand Down
Loading
Loading