diff --git a/apps/hyperdrive-trading/src/base/assertNever.ts b/apps/hyperdrive-trading/src/base/assertNever.ts new file mode 100644 index 000000000..066ab2348 --- /dev/null +++ b/apps/hyperdrive-trading/src/base/assertNever.ts @@ -0,0 +1,33 @@ +/** + * Helper function for exhaustive checks of discriminated unions. + * https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html + * + * @example + * + * type A = {type: 'a'}; + * type B = {type: 'b'}; + * type Union = A | B; + * + * function doSomething(arg: Union) { + * if (arg.type === 'a') { + * return something; + * } + * + * if (arg.type === 'b') { + * return somethingElse; + * } + * + * // TS will error if there are other types in the union + * // Will throw an Error when called at runtime. + * // Use `assertNever(arg, true)` instead to fail silently. + * return assertNever(arg); + * } + */ export function assertNever(value: never, noThrow?: boolean): never { + if (noThrow) { + return value; + } + + throw new Error( + `Unhandled discriminated union member: ${JSON.stringify(value)}`, + ); +} diff --git a/apps/hyperdrive-trading/src/ui/rewards/RewardsTooltip.tsx b/apps/hyperdrive-trading/src/ui/rewards/RewardsTooltip.tsx index c37f68b4e..d7e398f37 100644 --- a/apps/hyperdrive-trading/src/ui/rewards/RewardsTooltip.tsx +++ b/apps/hyperdrive-trading/src/ui/rewards/RewardsTooltip.tsx @@ -1,6 +1,7 @@ -import { findHyperdriveConfig } from "@hyperdrive/appconfig"; +import { appConfig, findHyperdriveConfig } from "@hyperdrive/appconfig"; import * as Tooltip from "@radix-ui/react-tooltip"; import { PropsWithChildren, ReactNode } from "react"; +import { assertNever } from "src/base/assertNever"; import { useAppConfig } from "src/ui/appconfig/useAppConfig"; import { useRewards } from "src/ui/rewards/useRewards"; import { Address } from "viem"; @@ -40,36 +41,55 @@ export function RewardsTooltip({ sideOffset={5} collisionPadding={12} > -
+

Rewards

{rewards?.map((reward) => { - if (reward.id === "MorphoFlatRate") { - return ( -
-
- Morpho logo - {reward.name} -
+ switch (reward.id) { + case "MorphoFlatRate": + return ( +
+
+ Morpho logo + {reward.name} +
-
-

- +{reward.amount} -

-

- per $1000 / yr -

+
+

+ +{reward.amount} +

+

+ per $1000 / yr +

+
+
+ ); + case "LineaLXPL": + return ( +
+
+ KelpDAO logo +

This pool is eligible for LXP-L rewards.

+
-
- ); + ); + default: + assertNever(reward.id); } })} diff --git a/apps/hyperdrive-trading/src/ui/rewards/useRewards.ts b/apps/hyperdrive-trading/src/ui/rewards/useRewards.ts index bdb618685..ef13065ca 100644 --- a/apps/hyperdrive-trading/src/ui/rewards/useRewards.ts +++ b/apps/hyperdrive-trading/src/ui/rewards/useRewards.ts @@ -4,7 +4,7 @@ import { HyperdriveConfig } from "@hyperdrive/appconfig"; import { usePoolInfo } from "src/ui/hyperdrive/hooks/usePoolInfo"; import { usePresentValue } from "src/ui/hyperdrive/hooks/usePresentValue"; import { Address } from "viem"; -import { mainnet } from "viem/chains"; +import { linea, mainnet } from "viem/chains"; // TODO @cashd: Move to AppConfig // https://github.com/delvtech/hyperdrive-frontend/issues/1341 @@ -17,19 +17,24 @@ const eligibleMarketsForMorphoRewards: Record = { ], }; +const eligibleMarketsForLineaRewards: Record = { + [linea.id]: [ + // 182d KelpDAO rsETH + "0xB56e0Bf37c4747AbbC3aA9B8084B0d9b9A336777", + ], +}; + // Source: https://docs.morpho.org/rewards/concepts/programs const MorphoFlatRatePerDay = 1.45e-4; const MorphoFlatRatePerYear = parseFixed(MorphoFlatRatePerDay * 365 * 1000); -type RewardType = "MorphoFlatRate"; +type RewardType = "MorphoFlatRate" | "LineaLXPL"; -type UseRewardsReturn = - | { - id: RewardType; - name: string; - amount: string; - }[] - | undefined; +type Reward = { + id: RewardType; + name: string; + amount: string; +}; function getWeightMorpho( poolConfig: PoolConfig, @@ -56,7 +61,7 @@ function getWeightMorpho( export function useRewards( hyperdrive: HyperdriveConfig, positionType: "short" | "lp", -): UseRewardsReturn { +): Reward[] | undefined { const { poolInfo } = usePoolInfo({ chainId: hyperdrive.chainId, hyperdriveAddress: hyperdrive.address, @@ -66,6 +71,9 @@ export function useRewards( hyperdriveAddress: hyperdrive.address, }); + const rewards = []; + + // Add any morpho rewards for this market if ( eligibleMarketsForMorphoRewards[hyperdrive.chainId]?.includes( hyperdrive.address, @@ -80,14 +88,29 @@ export function useRewards( ), ); - return [ - { - id: "MorphoFlatRate", - name: "MORPHO", - amount: morphoRate.format({ - decimals: 2, - }), - }, - ]; + const morphoReward: Reward = { + id: "MorphoFlatRate", + name: "MORPHO", + amount: morphoRate.format({ + decimals: 2, + }), + }; + rewards.push(morphoReward); + } + + // Add any linea rewards for this market + if ( + eligibleMarketsForLineaRewards[hyperdrive.chainId]?.includes( + hyperdrive.address, + ) + ) { + const lineaReward: Reward = { + id: "LineaLXPL", + name: "LXPL", + amount: "1", + }; + rewards.push(lineaReward); } + + return rewards; }