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?.map((reward) => {
- if (reward.id === "MorphoFlatRate") {
- return (
-
-
-
- {reward.name}
-
+ switch (reward.id) {
+ case "MorphoFlatRate":
+ return (
+
+
+
+ {reward.name}
+
-
-
- +{reward.amount}
-
-
- per $1000 / yr
-
+
+
+ +{reward.amount}
+
+
+ per $1000 / yr
+
+
+
+ );
+ case "LineaLXPL":
+ return (
+
+
+
+
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;
}