Skip to content

Commit

Permalink
Handle pointMultiplier reward type
Browse files Browse the repository at this point in the history
  • Loading branch information
DannyDelott committed Dec 17, 2024
1 parent 1cfeacc commit efc65e6
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 80 deletions.
9 changes: 9 additions & 0 deletions apps/hyperdrive-trading/src/base/assertNever.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function assertNever(value: never, noThrow?: boolean): never {
if (noThrow) {
return value;
}

throw new Error(
`Unhandled discriminated union member: ${JSON.stringify(value)}`,
);
}
185 changes: 106 additions & 79 deletions apps/hyperdrive-trading/src/ui/rewards/RewardsTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { SparklesIcon } from "@heroicons/react/16/solid";
import { ChartBarIcon } from "@heroicons/react/24/solid";
import * as Tooltip from "@radix-ui/react-tooltip";
import { PropsWithChildren, ReactNode } from "react";
import { assertNever } from "src/base/assertNever";
import { calculateMarketYieldMultiplier } from "src/hyperdrive/calculateMarketYieldMultiplier";
import { useIsNewPool } from "src/ui/hyperdrive/hooks/useIsNewPool";
import { useCurrentLongPrice } from "src/ui/hyperdrive/longs/hooks/useCurrentLongPrice";
Expand Down Expand Up @@ -94,93 +95,119 @@ export function RewardsTooltip({
</div>

{appConfigRewards?.map((reward) => {
if (reward.type === "info") {
return (
<div
key={reward.iconUrl}
className="flex flex-col items-start justify-start gap-2 border-b border-neutral-content/30 p-3 [&:nth-last-child(2)]:border-none"
>
<div className="flex items-center gap-4">
<img
src={reward.iconUrl}
alt={`${reward.message}`}
className="h-8"
/>
<p>{reward.message}</p>
switch (reward.type) {
case "info":
return (
<div
key={reward.iconUrl}
className="flex flex-col items-start justify-start gap-2 border-b border-neutral-content/30 p-3 [&:nth-last-child(2)]:border-none"
>
<div className="flex items-center gap-4">
<img
src={reward.iconUrl}
alt={`${reward.message}`}
className="h-8"
/>
<p>{reward.message}</p>
</div>
</div>
</div>
);
}
if (reward.type === "apy") {
// safe to cast because we assume all rewards tokens are
// available in appConfig
const token = getToken({
tokenAddress: reward.tokenAddress,
chainId: reward.chainId,
appConfig,
})!;
);
case "apy": {
// safe to cast because we assume all rewards tokens are
// available in appConfig
const token = getToken({
tokenAddress: reward.tokenAddress,
chainId: reward.chainId,
appConfig,
})!;

return (
<div
key={reward.tokenAddress}
className="flex items-center justify-between border-b border-neutral-content/30 p-3 [&:nth-last-child(2)]:border-none"
>
<div className="flex items-center gap-1">
<img
src={token.iconUrl}
alt={`${token.name} logo`}
className="h-4"
/>
{token.name}
</div>
return (
<div
key={reward.tokenAddress}
className="flex items-center justify-between border-b border-neutral-content/30 p-3 [&:nth-last-child(2)]:border-none"
>
<div className="flex items-center gap-1">
<img
src={token.iconUrl}
alt={`${token.name} logo`}
className="h-4"
/>
{token.name}
</div>

<div className="grid justify-items-end">
<p className="flex items-center gap-1">
+
{fixed(reward.apy).format({
percent: true,
decimals: 2,
})}
</p>
<div className="grid justify-items-end">
<p className="flex items-center gap-1">
+
{fixed(reward.apy).format({
percent: true,
decimals: 2,
})}
</p>
</div>
</div>
</div>
);
}
);
}
case "tokenAmount": {
// safe to cast because we assume all rewards tokens are
// available in appConfig
const token = getToken({
tokenAddress: reward.tokenAddress,
chainId: reward.chainId,
appConfig,
})!;
return (
<div
key={reward.tokenAddress}
className="flex items-center justify-between border-b border-neutral-content/30 p-3 [&:nth-last-child(2)]:border-none"
>
<div className="flex items-center gap-1">
<img
src={appConfig.protocols.morpho.iconUrl}
className="h-4"
/>
{token.name}
</div>

if (reward.type === "tokenAmount") {
// safe to cast because we assume all rewards tokens are
// available in appConfig
const token = getToken({
tokenAddress: reward.tokenAddress,
chainId: reward.chainId,
appConfig,
})!;
return (
<div
key={reward.tokenAddress}
className="flex items-center justify-between border-b border-neutral-content/30 p-3 [&:nth-last-child(2)]:border-none"
>
<div className="flex items-center gap-1">
<img
src={appConfig.protocols.morpho.iconUrl}
className="h-4"
/>
{token.name}
<div className="grid justify-items-end">
<p className="flex items-center gap-1">
+
{fixed(reward.tokensPerThousandUsd).format({
decimals: token.places,
})}
</p>
<p className="text-2xs text-neutral-content">
per $1000 / yr
</p>
</div>
</div>
);
}

<div className="grid justify-items-end">
<p className="flex items-center gap-1">
+
{fixed(reward.tokensPerThousandUsd).format({
decimals: token.places,
})}
</p>
<p className="text-2xs text-neutral-content">
per $1000 / yr
</p>
case "pointMultiplier":
return (
<div
key={reward.pointTokenLabel}
className="flex items-center justify-between border-b border-neutral-content/30 p-3 [&:nth-last-child(2)]:border-none"
>
<div className="flex items-center gap-1">
<img
src={reward.iconUrl}
alt={`${reward.pointTokenLabel} logo`}
className="h-4"
/>
{reward.pointTokenLabel}
</div>

<div className="grid justify-items-end">
<p className="flex items-center gap-1">
+{reward.pointMultiplier.toString()}x
</p>
</div>
</div>
</div>
);
);

default:
assertNever(reward);
}
})}

Expand Down
20 changes: 19 additions & 1 deletion packages/hyperdrive-appconfig/src/rewards/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ export interface TokenAmountReward {
chainId: number;
}

export interface PointMultiplierReward {
type: "pointMultiplier";
/**
* The multiplier for the point reward, eg: 2n means "2x"
*/
pointMultiplier: bigint;

/**
* The name for the point token, such as "Ether.fi Loyalty Points" or "SPIN Rewards"
*/
pointTokenLabel: string;
iconUrl: string;
}

/**
* Info rewards are used when reward details are unclear. They display a message
* to the user, such as "This pool is eligible for L-XPL Rewards."
Expand All @@ -37,7 +51,11 @@ export interface InfoReward {
iconUrl: string;
}

export type AnyReward = ApyReward | TokenAmountReward | InfoReward;
export type AnyReward =
| ApyReward
| TokenAmountReward
| PointMultiplierReward
| InfoReward;

export type RewardsResolver = (
publicClient: PublicClient,
Expand Down

0 comments on commit efc65e6

Please sign in to comment.