diff --git a/src/I18n/en.ts b/src/I18n/en.ts
index 1158f4f..2d9e22a 100644
--- a/src/I18n/en.ts
+++ b/src/I18n/en.ts
@@ -25,6 +25,7 @@ const en = {
title: "Dashboard",
explanation:
"Pending rewards are updated approximately every 4 hours, but are claimable onchain once every week.\nIf you don’t claim your rewards from a week, you may always claim them at a later time.",
+ reinvest: "",
},
tokens: {
headTitle: "Merkl | Tokens",
diff --git a/src/components/element/participate/Participate.tsx b/src/components/element/participate/Participate.tsx
index da44003..79c4cb3 100644
--- a/src/components/element/participate/Participate.tsx
+++ b/src/components/element/participate/Participate.tsx
@@ -198,7 +198,7 @@ export default function Participate({
)}
- {loading && (
+ {loading && !!merklConfig.deposit && (
diff --git a/src/components/element/reinvest/ReinvestBanner.tsx b/src/components/element/reinvest/ReinvestBanner.tsx
new file mode 100644
index 0000000..70e8ccc
--- /dev/null
+++ b/src/components/element/reinvest/ReinvestBanner.tsx
@@ -0,0 +1,59 @@
+import type { Opportunity } from "@merkl/api";
+import { Collapsible, EventBlocker, Group, Icon, Space, Text, mergeClass } from "dappkit";
+import { useEffect, useMemo, useState } from "react";
+import { I18n } from "../../../I18n";
+import OpportunityCell from "../../../components/element/opportunity/OpportunityCell";
+import merklConfig from "../../../config";
+import { OpportunityService } from "../../../modules/opportunity/opportunity.service";
+
+export default function ReinvestBanner() {
+ const [opportunities, setOpportunities] = useState();
+ const [isOpen, setIsOpen] = useState(true);
+
+ useEffect(() => {
+ if (!merklConfig.dashboard?.reinvestTokenAddress) return;
+ OpportunityService.getMany({
+ items: 3,
+ status: "LIVE",
+ }).then(({ opportunities }) => setOpportunities(opportunities));
+ }, []);
+
+ const cells = useMemo(
+ () =>
+ opportunities?.map(o => (
+
+
+
+ )),
+ [opportunities],
+ );
+
+ if (!merklConfig.dashboard?.reinvestTokenAddress) return;
+ return (
+ setIsOpen(!isOpen)}>
+
+
+
+
+ {I18n.trad.get.pages.dashboard.reinvest}
+
+
+
+
+ {isOpen && }
+
+ {cells}
+
+
+ );
+}
diff --git a/src/config/index.ts b/src/config/index.ts
index d630f53..baf6ab5 100644
--- a/src/config/index.ts
+++ b/src/config/index.ts
@@ -101,6 +101,7 @@ const defaultMerklConfig: MerklConfig = {
liquidityTab: {
enabled: false,
},
+ reinvestTokenAddress: "",
},
tagsDetails: {
token: {
diff --git a/src/config/type.ts b/src/config/type.ts
index 2c1558e..23be957 100644
--- a/src/config/type.ts
+++ b/src/config/type.ts
@@ -154,6 +154,10 @@ export type MerklConfig = {
liquidityTab: {
enabled: boolean;
};
+ /**
+ * Address of the token of user to reinvest in the dashboard
+ */
+ reinvestTokenAddress?: string;
};
tagsDetails: {
token: {
diff --git a/src/modules/user/routes/user.$address.header.tsx b/src/modules/user/routes/user.$address.header.tsx
index 7e89c65..1de2120 100644
--- a/src/modules/user/routes/user.$address.header.tsx
+++ b/src/modules/user/routes/user.$address.header.tsx
@@ -11,6 +11,7 @@ import Token from "../../../components/element/token/Token";
import merklConfig from "../../../config";
import useReward from "../../../hooks/resources/useReward";
import useRewards from "../../../hooks/resources/useRewards";
+import useBalances from "../../../hooks/useBalances";
import { RewardService } from "../../../modules/reward/reward.service";
import { TokenService } from "../../../modules/token/token.service";
import { UserService } from "../user.service";
@@ -48,7 +49,10 @@ export default function Index() {
const { rewards: raw, address, token: rawToken } = useLoaderData();
const fetcher = useFetcher();
+ const { reload: reloadBalances } = useBalances();
+
const onClaimSuccess = async (_hash: string) => {
+ reloadBalances();
await fetcher.submit(null, { method: "post", action: `/claim/${address}?chainId=${chainId}` });
};
diff --git a/src/modules/user/routes/user.$address.rewards.tsx b/src/modules/user/routes/user.$address.rewards.tsx
index 8272099..573f305 100644
--- a/src/modules/user/routes/user.$address.rewards.tsx
+++ b/src/modules/user/routes/user.$address.rewards.tsx
@@ -1,14 +1,17 @@
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json, useLoaderData, useOutletContext } from "@remix-run/react";
-import { Container, Group, Icon, Space, Text } from "dappkit";
+import { Container, Fmt, Group, Icon, Space, Text, useWalletContext } from "dappkit";
+import { useMemo } from "react";
import { isAddress } from "viem";
import { I18n } from "../../../I18n";
+import ReinvestBanner from "../../../components/element/reinvest/ReinvestBanner";
import ClaimRewardsLibrary from "../../../components/element/rewards/ClaimRewardsLibrary";
+import merklConfig from "../../../config";
+import useBalances from "../../../hooks/useBalances";
import type { OutletContextRewards } from "./user.$address.header";
export async function loader({ params: { address } }: LoaderFunctionArgs) {
if (!address || !isAddress(address)) throw "";
-
return json({ address });
}
@@ -16,8 +19,22 @@ export default function Index() {
const { address } = useLoaderData();
const { rewards: sortedRewards, onClaimSuccess } = useOutletContext();
+ const { chainId } = useWalletContext();
+ const { balances, loading: balanceLoading } = useBalances(chainId);
+
+ const tokenBalance = useMemo(() => {
+ if (!balances || balanceLoading) return 0;
+ const token = balances?.find(
+ balance => balance?.address?.toLowerCase() === merklConfig.dashboard?.reinvestTokenAddress?.toLowerCase(),
+ );
+ if (!token) return 0;
+ return Fmt.toNumber(token?.balance, token?.decimals);
+ }, [balances, balanceLoading]);
+
return (
+
+ {!!I18n.trad.get.pages.dashboard.reinvest && tokenBalance > 0 && }
{!!I18n.trad.get.pages.dashboard.explanation && (