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
4 changes: 4 additions & 0 deletions apps/web/src/app/[lang]/dho/[id]/@tab/treasury/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AssetsSection,
TransactionsSection,
SpaceTabAccessWrapper,
SpacePendingRewardsSection,
} from '@hypha-platform/epics';
import { getDhoPathTreasury } from './constants';
import { findSpaceBySlug } from '@hypha-platform/core/server';
Expand All @@ -26,6 +27,9 @@ export default async function TreasuryPage(props: PageProps) {
spaceSlug={id}
>
<div className="flex flex-col gap-6 py-4">
<SpacePendingRewardsSection
web3SpaceId={spaceFromDb?.web3SpaceId as number}
/>
<AssetsSection
basePath={basePath}
web3SpaceId={spaceFromDb?.web3SpaceId as number}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ import { Empty } from '../../../common';
const HYPHA_TOKEN_ADDRESS = '0x8b93862835C36e9689E9bb1Ab21De3982e266CD3';
const MIN_REWARD_CLAIM_VALUE = 0.01;

const HYPHA_REWARDS_FALLBACK = {
icon: '/placeholder/hypha-token-icon.svg',
name: 'Hypha',
symbol: 'HYPHA',
value: 0,
address: HYPHA_TOKEN_ADDRESS,
};

type PendingRewardsSectionProps = {
person: Person;
isMyProfile?: boolean;
Expand Down Expand Up @@ -50,11 +58,10 @@ export const PendingRewardsSection: FC<PendingRewardsSectionProps> = ({
pendingRewards !== undefined ? Number(pendingRewards / 10n ** 18n) : 0;

const hyphaTokenAsset =
originalAsset && pendingRewards !== undefined
? {
...originalAsset,
value: parsedRewardValue,
}
pendingRewards !== undefined
? originalAsset
? { ...originalAsset, value: parsedRewardValue }
: { ...HYPHA_REWARDS_FALLBACK, value: parsedRewardValue }
: undefined;
useEffect(() => {
if (parsedRewardValue >= MIN_REWARD_CLAIM_VALUE) {
Expand All @@ -67,6 +74,7 @@ export const PendingRewardsSection: FC<PendingRewardsSectionProps> = ({
!(parsedRewardValue >= MIN_REWARD_CLAIM_VALUE) ||
isClaiming ||
pendingRewards === undefined;

const onHandleClaim = useCallback(async () => {
try {
const txHash = await claim();
Expand Down Expand Up @@ -99,17 +107,24 @@ export const PendingRewardsSection: FC<PendingRewardsSectionProps> = ({
</Button>
</div>
<div className="w-full">
{!isAuthenticated ? (
{isLoading ? (
<div className="w-full grid grid-cols-1 sm:grid-cols-3 gap-2 mt-2">
<AssetCard isLoading />
</div>
) : !isAuthenticated ? (
<Empty>
<p>No rewards found for this user</p>
</Empty>
) : (
<>
<div className="w-full grid grid-cols-1 sm:grid-cols-3 gap-2 mt-2">
<AssetCard {...hyphaTokenAsset} isLoading={isLoadingAssets} />
</div>
{isLoading && <AssetCard isLoading />}
</>
<div className="w-full grid grid-cols-1 sm:grid-cols-3 gap-2 mt-2">
<AssetCard
{...(hyphaTokenAsset ?? {
...HYPHA_REWARDS_FALLBACK,
value: 0,
})}
isLoading={isLoadingAssets}
/>
</div>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
'use client';

import { FC, useCallback, useEffect, useState } from 'react';
import { SectionFilter } from '@hypha-platform/ui/server';
import {
usePendingRewards,
useSpaceDetailsWeb3Rpc,
} from '@hypha-platform/core/client';
import { AssetCard } from './asset-card';
import { useAssetsSection, useTokenSupply } from '../../hooks';
import { Button } from '@hypha-platform/ui';
import { Loader2 } from 'lucide-react';
import { useAuthentication } from '@hypha-platform/authentication';
import { Empty } from '../../../common';
import { useSpaceMember } from '../../../spaces';
import { useParams } from 'next/navigation';
import { useSWRConfig } from 'swr';

const HYPHA_TOKEN_ADDRESS = '0x8b93862835C36e9689E9bb1Ab21De3982e266CD3';
const MIN_REWARD_CLAIM_VALUE = 0.01;
Comment on lines +19 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for all occurrences of HYPHA_TOKEN_ADDRESS and MIN_REWARD_CLAIM_VALUE
rg -n "HYPHA_TOKEN_ADDRESS|MIN_REWARD_CLAIM_VALUE" --type=ts --type=tsx -g '!node_modules'

Repository: hypha-dao/hypha-web

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Search for constants using glob patterns instead of file type
rg "HYPHA_TOKEN_ADDRESS|MIN_REWARD_CLAIM_VALUE" -g "*.ts" -g "*.tsx" -n

Repository: hypha-dao/hypha-web

Length of output: 1685


🏁 Script executed:

#!/bin/bash
# Check what files exist in the assets directory
ls -la packages/epics/src/treasury/components/assets/

Repository: hypha-dao/hypha-web

Length of output: 604


Extract duplicated constants into a shared module.

HYPHA_TOKEN_ADDRESS and MIN_REWARD_CLAIM_VALUE are duplicated verbatim in both pending-rewards-section.tsx (lines 13–14) and space-pending-rewards-section.tsx (lines 19–20). Extract them into a shared constants file (e.g., constants.ts in the same directory) to avoid drift and improve maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/epics/src/treasury/components/assets/space-pending-rewards-section.tsx`
around lines 19 - 20, Extract the duplicated HYPHA_TOKEN_ADDRESS and
MIN_REWARD_CLAIM_VALUE into a shared module (e.g., create a constants.ts next to
these components), export both constants from that module, and replace the
inline definitions in space-pending-rewards-section.tsx and
pending-rewards-section.tsx with imports from the new constants module; ensure
the exported names match HYPHA_TOKEN_ADDRESS and MIN_REWARD_CLAIM_VALUE so
existing usages in those components remain unchanged.


const HYPHA_REWARDS_FALLBACK = {
icon: '/placeholder/hypha-token-icon.svg',
name: 'Hypha',
symbol: 'HYPHA',
value: 0,
address: HYPHA_TOKEN_ADDRESS,
};

type SpacePendingRewardsSectionProps = {
web3SpaceId: number;
};

export const SpacePendingRewardsSection: FC<
SpacePendingRewardsSectionProps
> = ({ web3SpaceId }) => {
const { id: spaceSlug } = useParams<{ id: string }>();
const { mutate } = useSWRConfig();
const { isAuthenticated } = useAuthentication();
const { spaceDetails } = useSpaceDetailsWeb3Rpc({ spaceId: web3SpaceId });
const { isMember } = useSpaceMember({ spaceId: web3SpaceId });
const executor = spaceDetails?.executor as `0x${string}` | undefined;

const { filteredAssets, isLoading: isLoadingAssets } = useAssetsSection();
const { supply: hyphaTotalSupply } = useTokenSupply(
HYPHA_TOKEN_ADDRESS as `0x${string}`,
);

const {
pendingRewards,
isLoading,
claim,
waitForClaimReceipt,
isClaiming,
updatePendingRewards,
} = usePendingRewards({ user: executor });

const [hasClaimed, setHasClaimed] = useState(false);

const originalAsset = filteredAssets?.find(
(a) => a.address === HYPHA_TOKEN_ADDRESS,
);

const parsedRewardValue =
pendingRewards !== undefined ? Number(pendingRewards / 10n ** 18n) : 0;
Comment on lines +64 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Same BigInt truncation issue as pending-rewards-section.tsx.

Number(pendingRewards / 10n ** 18n) truncates all sub-token fractions to 0, which will cause hasNoRewards (line 84-85) to hide the section when fractional rewards exist. See the detailed comment on pending-rewards-section.tsx line 70-88.

Proposed fix
  const parsedRewardValue =
-    pendingRewards !== undefined ? Number(pendingRewards / 10n ** 18n) : 0;
+    pendingRewards !== undefined
+      ? Number(pendingRewards) / Number(10n ** 18n)
+      : 0;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/epics/src/treasury/components/assets/space-pending-rewards-section.tsx`
around lines 53 - 54, parsedRewardValue currently uses integer BigInt division
(Number(pendingRewards / 10n ** 18n)) which truncates fractional token amounts
and makes hasNoRewards treat fractional rewards as zero; change the computation
to perform the division in floating point by converting the BigInt to a Number
first (e.g., Number(pendingRewards) / 1e18) or otherwise compute a decimal
string, so parsedRewardValue retains fractions; update the assignment referenced
by parsedRewardValue and ensure hasNoRewards (the boolean that hides the
section) uses the new parsedRewardValue logic.


const baseHyphaAsset = originalAsset
? { ...originalAsset, value: parsedRewardValue }
: { ...HYPHA_REWARDS_FALLBACK, value: parsedRewardValue };

const supplyFromAsset = (originalAsset as { supply?: { total: number } })
?.supply;
const supply =
supplyFromAsset ??
(hyphaTotalSupply !== undefined ? { total: hyphaTotalSupply } : undefined);

const hyphaTokenAsset =
pendingRewards !== undefined ? { ...baseHyphaAsset, supply } : undefined;

useEffect(() => {
if (parsedRewardValue >= MIN_REWARD_CLAIM_VALUE) {
setHasClaimed(false);
}
}, [parsedRewardValue]);

const updateSpaceAssets = useCallback(() => {
if (spaceSlug) {
mutate([`/api/v1/spaces/${spaceSlug}/assets`]);
}
}, [mutate, spaceSlug]);

const disableClaimButton =
hasClaimed ||
!(parsedRewardValue >= MIN_REWARD_CLAIM_VALUE) ||
isClaiming ||
pendingRewards === undefined;

const canClaim = isAuthenticated && isMember;

const onHandleClaim = useCallback(async () => {
if (!canClaim || !executor) return;
try {
const txHash = await claim();
await waitForClaimReceipt(txHash as `0x${string}`);
await updatePendingRewards();
await updateSpaceAssets();
setHasClaimed(true);
} catch (error) {
console.error('Claim failed:', error);
}
}, [
canClaim,
executor,
claim,
waitForClaimReceipt,
updatePendingRewards,
updateSpaceAssets,
]);

return (
<div className="flex flex-col w-full justify-center items-center gap-3">
<div className="w-full flex justify-between">
<SectionFilter label="Rewards" />
<Button
title={
!isAuthenticated
? 'Please sign in to claim rewards'
: !isMember
? 'Only space members can claim rewards'
: disableClaimButton
? 'The reward value must be greater than 0'
: ''
Comment on lines +130 to +132
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Align disabled tooltip copy with the actual minimum claim threshold.

At Line 131, the message says “greater than 0” but the guard uses MIN_REWARD_CLAIM_VALUE = 0.01, which is inconsistent for users.

Suggested copy fix
-              ? 'The reward value must be greater than 0'
+              ? 'The reward value must be at least 0.01'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/epics/src/treasury/components/assets/space-pending-rewards-section.tsx`
around lines 130 - 132, The tooltip text currently says "The reward value must
be greater than 0" while the guard uses MIN_REWARD_CLAIM_VALUE = 0.01; update
the disabled tooltip (the ternary that sets the message when disableClaimButton
is true) to reflect the actual threshold (e.g., "The reward value must be at
least {MIN_REWARD_CLAIM_VALUE}") and interpolate the MIN_REWARD_CLAIM_VALUE
constant instead of hardcoding a number so the message stays in sync with the
guard (refer to disableClaimButton and MIN_REWARD_CLAIM_VALUE in
space-pending-rewards-section.tsx).

}
disabled={!canClaim || disableClaimButton}
onClick={onHandleClaim}
>
{isClaiming && <Loader2 className="animate-spin w-4 h-4" />}
Claim
</Button>
</div>
<div className="w-full">
{isLoading || !executor ? (
<div className="w-full grid grid-cols-1 sm:grid-cols-3 gap-2 mt-2">
<AssetCard isLoading />
</div>
) : !isAuthenticated ? (
<Empty>
<p>Sign in to view space rewards</p>
</Empty>
) : (
<div className="w-full grid grid-cols-1 sm:grid-cols-3 gap-2 mt-2">
<AssetCard
{...(hyphaTokenAsset ?? {
...HYPHA_REWARDS_FALLBACK,
value: 0,
supply:
hyphaTotalSupply !== undefined
? { total: hyphaTotalSupply }
: undefined,
})}
isLoading={isLoadingAssets}
/>
</div>
)}
</div>
</div>
);
};
1 change: 1 addition & 0 deletions packages/epics/src/treasury/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from './common/token-max-supply-field';
export * from './assets/user-assets-section';
export * from './requests/user-transactions-section';
export * from './assets/pending-rewards-section';
export * from './assets/space-pending-rewards-section';
export * from './common/token-max-supply-type-field';
export * from './common/transfer-whitelist-field-array';
export * from './common/general-token-settings';
Expand Down