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
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
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { ChainId, ProtocolAction, Stake } from '@aave/contract-helpers';
import { ChainId, Stake } from '@aave/contract-helpers';
import { normalize, valueToBigNumber } from '@aave/math-utils';
import { Trans } from '@lingui/macro';
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,11 +36,10 @@ export const SavingsGhoModalDepositContent = () => {
store.currentChainId,
])
);
const { data: meritIncentives } = useMeritIncentives({
symbol: 'GHO',
market: currentMarketData.market,
protocolAction: ProtocolAction.stake,
});
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 @@ -119,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
16 changes: 0 additions & 16 deletions src/hooks/useMeritIncentives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,6 @@ export type MeritAction = string;

export const ENABLE_SELF_CAMPAIGN = true;

// Merit action keys that should only appear in specific protocol contexts.
const MERIT_ACTION_PROTOCOL_ALLOWLIST: Record<string, Set<ProtocolAction>> = {
'ethereum-sgho': new Set([ProtocolAction.stake]),
'ethereum-stkgho': new Set([ProtocolAction.umbrellaStake]),
};

export type MeritIncentivesBreakdown = {
protocolAPY: number;
protocolIncentivesAPR: number;
Expand Down Expand Up @@ -150,16 +144,6 @@ export const useMeritIncentives = ({
const rewardTokenAddress = enriched.rewardTokenAddress ?? '';
const rewardTokenSymbol = enriched.rewardTokenSymbol ?? '';

if (enriched.actionKey) {
const allowedProtocolActions = MERIT_ACTION_PROTOCOL_ALLOWLIST[enriched.actionKey];
if (
allowedProtocolActions &&
(!protocolAction || !allowedProtocolActions.has(protocolAction))
) {
continue;
}
}

if (m.__typename === 'MeritSupplyIncentive') {
apr = parseFloat(m.extraSupplyApr.formatted);
} else if (m.__typename === 'MeritBorrowIncentive') {
Expand Down
113 changes: 113 additions & 0 deletions src/hooks/useSavingsGhoIncentive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { useQuery } from '@tanstack/react-query';

const DEFAULT_ENDPOINT = 'https://api.v3.staging.aave.com/graphql';
const GRAPHQL_ENDPOINT = process.env.NEXT_PUBLIC_AAVE_V3_API_URL ?? DEFAULT_ENDPOINT;
const MAINNET_CHAIN_ID = 1;

type PercentValue = {
decimals: number;
formatted: string;
raw: string;
value: string;
};

export type MeritSavingsGhoIncentive = {
__typename: 'MeritSavingsGhoIncentive';
actionKey?: string | null;
apr: PercentValue;
claimLink: string;
customForumLink?: string | null;
customMessage?: string | null;
rewardTokenAddress?: string | null;
rewardTokenSymbol?: string | null;
};

type SavingsGhoIncentiveResponse = {
data?: {
savingsGhoIncentive?: MeritSavingsGhoIncentive | null;
};
errors?: { message: string }[];
};

export type SavingsGhoAprData = MeritSavingsGhoIncentive & {
aprDecimal: string;
aprPercentage: number;
};

type UseSavingsGhoIncentiveArgs = {
chainId?: number;
enabled?: boolean;
};

const SAVINGS_GHO_INCENTIVE_QUERY = `
query SavingsGhoIncentive($chainId: ChainId) {
savingsGhoIncentive(chainId: $chainId) {
__typename
actionKey
apr {
decimals
formatted
raw
value
}
claimLink
customForumLink
customMessage
rewardTokenAddress
rewardTokenSymbol
}
}
`;

export const useSavingsGhoIncentive = ({
chainId = MAINNET_CHAIN_ID,
enabled = true,
}: UseSavingsGhoIncentiveArgs = {}) => {
return useQuery<SavingsGhoAprData | null>({
queryKey: ['savingsGhoIncentive', chainId],
staleTime: 1000 * 60 * 5,
enabled: enabled && Boolean(chainId),
queryFn: async () => {
const response = await fetch(GRAPHQL_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: SAVINGS_GHO_INCENTIVE_QUERY,
variables: { chainId },
}),
});

if (!response.ok) {
throw new Error(`Savings GHO incentive query failed: ${response.status}`);
}

const body = (await response.json()) as SavingsGhoIncentiveResponse;

if (body.errors?.length) {
throw new Error(
`Savings GHO incentive query returned errors: ${body.errors
.map((e) => e.message)
.join(', ')}`
);
}

const incentive = body.data?.savingsGhoIncentive;
if (!incentive) {
return null;
}

const aprDecimal = incentive.apr.value;
const aprPercentage = parseFloat(aprDecimal) * 100;

if (!Number.isFinite(aprPercentage) || aprPercentage <= 0) {
return null;
}

return {
...incentive,
aprDecimal,
aprPercentage,
};
},
});
};
64 changes: 0 additions & 64 deletions src/hooks/useStakeTokenAPR.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/locales/el/messages.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/locales/en/messages.js

Large diffs are not rendered by default.

Loading
Loading