Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor hooks #2642

Merged
merged 17 commits into from
Jan 15, 2025
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
refactor contributions
aymericdelab committed Jan 14, 2025
commit e2b67fe5ad8180b967a4113789741efc5702e650
96 changes: 9 additions & 87 deletions client/apps/game/src/hooks/helpers/use-contributions.tsx
Original file line number Diff line number Diff line change
@@ -1,98 +1,20 @@
import { configManager } from "@/dojo/setup";
import { useDojo } from "@/hooks/context/dojo-context";
import { divideByPrecision } from "@/ui/utils/utils";
import { ClientComponents, ContractAddress, ID, Resource } from "@bibliothecadao/eternum";
import { ClientComponents, ContractAddress, ID } from "@bibliothecadao/eternum";
import { useEntityQuery } from "@dojoengine/react";
import { ComponentValue, HasValue, getComponentValue, runQuery } from "@dojoengine/recs";
import { useCallback } from "react";
import { ComponentValue, HasValue, getComponentValue } from "@dojoengine/recs";

export const useContributions = () => {
export const usePlayerContributions = (playerAddress: ContractAddress, hyperstructureEntityId: ID) => {
const {
setup: {
components: { Contribution },
},
} = useDojo();

const getContributions = (hyperstructureEntityId: ID) => {
const contributionsToHyperstructure = Array.from(
runQuery([HasValue(Contribution, { hyperstructure_entity_id: hyperstructureEntityId })]),
).map((id) => getComponentValue(Contribution, id));
const contributionsToHyperstructure = useEntityQuery([
HasValue(Contribution, { hyperstructure_entity_id: hyperstructureEntityId, player_address: playerAddress }),
])
.map((id) => getComponentValue(Contribution, id))
.filter((x): x is ComponentValue<ClientComponents["Contribution"]["schema"]> => x !== undefined);

return contributionsToHyperstructure as ComponentValue<ClientComponents["Contribution"]["schema"]>[];
};

const useContributionsByPlayerAddress = (playerAddress: ContractAddress, hyperstructureEntityId: ID) => {
const contributionsToHyperstructure = useEntityQuery([
HasValue(Contribution, { hyperstructure_entity_id: hyperstructureEntityId, player_address: playerAddress }),
])
.map((id) => getComponentValue(Contribution, id))
.filter((x): x is ComponentValue<ClientComponents["Contribution"]["schema"]> => x !== undefined);

return contributionsToHyperstructure;
};

const getContributionsTotalPercentage = (hyperstructureId: number, contributions: Resource[]) => {
const totalPlayerContribution = divideByPrecision(
contributions.reduce((acc, { amount, resourceId }) => {
return acc + amount * configManager.getResourceRarity(resourceId);
}, 0),
);

const totalHyperstructureContribution = configManager.getHyperstructureTotalContributableAmount(hyperstructureId);

return totalPlayerContribution / totalHyperstructureContribution;
};

return {
getContributions,
useContributionsByPlayerAddress,
getContributionsTotalPercentage,
};
};

export const useGetHyperstructuresWithContributionsFromPlayer = () => {
const {
account: { account },
setup: {
components: { Contribution },
},
} = useDojo();

const getContributions = useCallback(() => {
const entityIds = runQuery([HasValue(Contribution, { player_address: ContractAddress(account.address) })]);
const hyperstructureEntityIds = Array.from(entityIds).map(
(entityId) => getComponentValue(Contribution, entityId)?.hyperstructure_entity_id ?? 0,
);
return new Set(hyperstructureEntityIds);
}, [account.address]);

return getContributions;
};

export const useGetUnregisteredContributions = () => {
const {
account: { account },
setup: {
components: { LeaderboardRegisterContribution },
},
} = useDojo();
const getContributions = useGetHyperstructuresWithContributionsFromPlayer();

const getUnregisteredContributions = useCallback(() => {
const registeredContributionsEntities = runQuery([
HasValue(LeaderboardRegisterContribution, { address: ContractAddress(account.address) }),
]);
const registeredContributions = Array.from(registeredContributionsEntities)
.map((entityId) => getComponentValue(LeaderboardRegisterContribution, entityId)?.hyperstructure_entity_id)
.filter((x): x is number => x !== undefined);
console.log("registeredContributions", registeredContributions);
const hyperstructuresContributedTo = Array.from(getContributions());
console.log("hyperstructuresContributedTo", hyperstructuresContributedTo);
return hyperstructuresContributedTo.filter(
(hyperstructureEntityId) =>
!registeredContributions.some((contribution) => contribution === hyperstructureEntityId),
);
}, [getContributions]);

return getUnregisteredContributions;
return contributionsToHyperstructure;
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useContributions } from "@/hooks/helpers/use-contributions";
import { useDojo } from "@/hooks/context/dojo-context";
import { useRealm } from "@/hooks/helpers/use-realm";
import { ResourceIcon } from "@/ui/elements/resource-icon";
import { SelectResource } from "@/ui/elements/select-resource";
import { copyPlayerAddressToClipboard, currencyIntlFormat, divideByPrecision, formatNumber } from "@/ui/utils/utils";
import { ContractAddress, ID, ResourcesIds } from "@bibliothecadao/eternum";
import { ContractAddress, ID, LeaderboardManager, ResourcesIds } from "@bibliothecadao/eternum";
import { useMemo, useState } from "react";

export const ContributionSummary = ({
@@ -13,7 +13,14 @@ export const ContributionSummary = ({
hyperstructureEntityId: ID;
className?: string;
}) => {
const { getContributions, getContributionsTotalPercentage } = useContributions();
const {
setup: { components },
} = useDojo();

const leaderboardManager = useMemo(() => {
return LeaderboardManager.instance(components);
}, [components]);

const { getAddressName } = useRealm();

const [showContributions, setShowContributions] = useState(false);
@@ -24,7 +31,10 @@ export const ContributionSummary = ({
resourceId: number;
};

const contributions = getContributions(hyperstructureEntityId);
const contributions = useMemo(() => {
return leaderboardManager.getContributions(hyperstructureEntityId);
}, [leaderboardManager, hyperstructureEntityId]);

const groupedContributions = contributions.reduce<Record<string, Record<number, bigint>>>((acc, contribution) => {
const { player_address, resource_type, amount } = contribution;
const playerAddressString = player_address.toString();
@@ -59,7 +69,10 @@ export const ContributionSummary = ({
playerAddress,
resources,
percentage:
getContributionsTotalPercentage(hyperstructureEntityId, resourceContributions[playerAddress]) * 100,
leaderboardManager.getContributionsTotalPercentage(
hyperstructureEntityId,
resourceContributions[playerAddress],
) * 100,
Comment on lines +70 to +73
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

Add null check to prevent NaN in percentage calculation

The current implementation could return NaN if totalHyperstructureContribution is zero. The suggested fix using || 0 is appropriate as it will handle this edge case and any other potential undefined/NaN scenarios, while also improving code readability.

🔗 Analysis chain

Handle potential undefined values in percentage calculation.

Ensure that getContributionsTotalPercentage returns a valid number to prevent potential issues when multiplying by 100.

Apply this diff to add a default value:

- percentage:
-   leaderboardManager.getContributionsTotalPercentage(
-     hyperstructureEntityId,
-     resourceContributions[playerAddress],
-   ) * 100,
+ const totalPercentage = leaderboardManager.getContributionsTotalPercentage(
+   hyperstructureEntityId,
+   resourceContributions[playerAddress],
+ ) || 0;
+ percentage: totalPercentage * 100,
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Find implementation of getContributionsTotalPercentage
ast-grep --pattern 'getContributionsTotalPercentage($$$) {
  $$$
}'

# Search for similar percentage calculations
rg "getContributionsTotalPercentage.*\*\s*100" -A 2

# Look for the leaderboardManager class definition
ast-grep --pattern 'class $_ implements LeaderboardManager {
  $$$
}'

Length of output: 195


Script:

#!/bin/bash
# Find any mentions of getContributionsTotalPercentage
rg "getContributionsTotalPercentage"

# Find LeaderboardManager interface or class
rg "LeaderboardManager"

# Find files that might contain the implementation
fd -e ts -e tsx "leaderboard"

Length of output: 4613


Script:

#!/bin/bash
# Get the implementation of getContributionsTotalPercentage from LeaderboardManager
cat client/sdk/packages/eternum/src/modelManager/LeaderboardManager.ts | grep -A 10 "getContributionsTotalPercentage"

# Get the implementation from LeaderboardUtils
cat client/sdk/packages/eternum/src/modelManager/utils/LeaderboardUtils.ts | grep -A 15 "getContributionsTotalPercentage"

Length of output: 1522

}))
.filter(({ resources }) =>
selectedResource ? resources[selectedResource] > 0n : Object.values(resources).some((amount) => amount > 0n),
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { configManager } from "@/dojo/setup";
import { useDojo } from "@/hooks/context/dojo-context";
import { useContributions } from "@/hooks/helpers/use-contributions";
import { usePlayerContributions } from "@/hooks/helpers/use-contributions";
import { useEntitiesUtils } from "@/hooks/helpers/use-entities";
import { useGuilds } from "@/hooks/helpers/use-guilds";
import {
@@ -66,9 +66,7 @@ export const HyperstructurePanel = ({ entity }: any) => {

const progresses = useHyperstructureProgress(entity.entity_id);

const { useContributionsByPlayerAddress } = useContributions();

const myContributions = useContributionsByPlayerAddress(BigInt(account.address), entity.entity_id);
const myContributions = usePlayerContributions(BigInt(account.address), entity.entity_id);

const updates = useHyperstructureUpdates(entity.entity_id);

33 changes: 16 additions & 17 deletions client/apps/game/src/ui/modules/rewards/rewards.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { useDojo } from "@/hooks/context/dojo-context";
import {
useGetHyperstructuresWithContributionsFromPlayer,
useGetUnregisteredContributions,
} from "@/hooks/helpers/use-contributions";
import { useGetPlayerEpochs, useGetUnregisteredEpochs } from "@/hooks/helpers/use-hyperstructures";
import { useGetUnregisteredEpochs } from "@/hooks/helpers/use-hyperstructures";
import { usePrizePool } from "@/hooks/helpers/use-rewards";
import useUIStore from "@/hooks/store/use-ui-store";
import { HintSection } from "@/ui/components/hints/hint-modal";
import { rewards } from "@/ui/components/navigation/config";
import { OSWindow } from "@/ui/components/navigation/os-window";
import Button from "@/ui/elements/button";
import { formatTime, getEntityIdFromKeys } from "@/ui/utils/utils";
import { ContractAddress } from "@bibliothecadao/eternum";
import { ContractAddress, LeaderboardManager } from "@bibliothecadao/eternum";
import { useComponentValue, useEntityQuery } from "@dojoengine/react";
import { Has, getComponentValue, runQuery } from "@dojoengine/recs";
import { useCallback, useEffect, useMemo, useState } from "react";
@@ -26,16 +22,18 @@ export const Rewards = () => {
const {
account: { account },
setup: {
components: {
AddressName,
LeaderboardEntry,
LeaderboardRegistered,
events: { GameEnded },
},
components,
systemCalls: { register_to_leaderboard, claim_leaderboard_rewards },
},
} = useDojo();

const {
LeaderboardEntry,
LeaderboardRegistered,
AddressName,
events: { GameEnded },
} = components;

const [timeRemaining, setTimeRemaining] = useState<string>("");
const [isLoading, setIsLoading] = useState(false);
const [registrationTimeRemaining, setRegistrationTimeRemaining] = useState<string>("");
@@ -45,9 +43,10 @@ export const Rewards = () => {
const togglePopup = useUIStore((state) => state.togglePopup);
const isOpen = useUIStore((state) => state.isPopupOpen(rewards));

const getContributions = useGetHyperstructuresWithContributionsFromPlayer();
const getEpochs = useGetPlayerEpochs();
const getUnregisteredContributions = useGetUnregisteredContributions();
const leaderboardManager = useMemo(() => {
return LeaderboardManager.instance(components);
}, [components]);

const getUnregisteredEpochs = useGetUnregisteredEpochs();

const gameEndedEntityId = useEntityQuery([Has(GameEnded)]);
@@ -61,7 +60,7 @@ export const Rewards = () => {
const registerToLeaderboard = useCallback(async () => {
setIsLoading(true);
const epochs = getUnregisteredEpochs();
const contributions = getUnregisteredContributions();
const contributions = leaderboardManager.getPlayerUnregistredContributions(ContractAddress(account.address));

try {
await register_to_leaderboard({
@@ -74,7 +73,7 @@ export const Rewards = () => {
} finally {
setIsLoading(false);
}
}, [getContributions, getEpochs]);
}, [leaderboardManager]);

const claimRewards = useCallback(async () => {
setIsLoading(true);
33 changes: 7 additions & 26 deletions client/apps/game/src/ui/modules/social/end-season-button.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { configManager } from "@/dojo/setup";
import { useDojo } from "@/hooks/context/dojo-context";
import { useGetHyperstructuresWithContributionsFromPlayer } from "@/hooks/helpers/use-contributions";
import { useGetPlayerEpochs } from "@/hooks/helpers/use-hyperstructures";
import { useLeaderBoardStore } from "@/hooks/store/use-leaderboard-store";
import useUIStore from "@/hooks/store/use-ui-store";
import Button from "@/ui/elements/button";
import { ContractAddress } from "@bibliothecadao/eternum";
import { ContractAddress, LeaderboardManager } from "@bibliothecadao/eternum";
import clsx from "clsx";
import { useCallback, useMemo } from "react";

@@ -22,7 +21,6 @@ export const EndSeasonButton = () => {
const structureEntityId = useUIStore((state) => state.structureEntityId);
const nextBlockTimestamp = useUIStore.getState().nextBlockTimestamp!;

const getContributions = useGetHyperstructuresWithContributionsFromPlayer();
const getEpochs = useGetPlayerEpochs();

const pointsForWin = configManager.getHyperstructureConfig().pointsForWin;
@@ -50,35 +48,19 @@ export const EndSeasonButton = () => {
if (!hasReachedFinalPoints) {
return;
}
const contributions = Array.from(getContributions());
const contributions = Array.from(
LeaderboardManager.instance(setup.components).getHyperstructuresWithContributionsFromPlayer(
ContractAddress(account.address),
),
);
const epochs = getEpochs();

await setup.systemCalls.end_game({
signer: account,
hyperstructure_contributed_to: contributions,
hyperstructure_shareholder_epochs: epochs,
});
}, [hasReachedFinalPoints, getContributions]);

const logPoints = useCallback(async () => {
const contributions = Array.from(getContributions());
const epochs = getEpochs();
console.log({ contributions, epochs });

const points = (await setup.systemCalls.get_points({
signer: account,
player_address: account.address,
hyperstructure_contributed_to: contributions,
hyperstructure_shareholder_epochs: epochs,
})) as [string, string, string, string];

console.log({
contribution_points: BigInt(points[0]).toLocaleString(),
share_points: BigInt(points[1]).toLocaleString(),
total_points: BigInt(points[2]).toLocaleString(),
points_for_win: BigInt(points[3]).toLocaleString(),
});
}, [hasReachedFinalPoints, getContributions]);
}, [hasReachedFinalPoints]);

return (
<Button
@@ -102,7 +84,6 @@ export const EndSeasonButton = () => {
}}
style={{ background: gradient }}
onClick={endGame}
// onClick={logPoints}
>
End season
</Button>
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useDojo } from "@/hooks/context/dojo-context";
import { useArmiesAtPosition } from "@/hooks/helpers/use-armies";
import { useGetHyperstructuresWithContributionsFromPlayer } from "@/hooks/helpers/use-contributions";
import { useEntitiesUtils } from "@/hooks/helpers/use-entities";
import { useFragmentMines } from "@/hooks/helpers/use-fragment-mines";
import { useGuilds } from "@/hooks/helpers/use-guilds";
@@ -30,6 +29,7 @@ import { Tabs } from "../../elements/tab";

export const WorldStructuresMenu = ({ className }: { className?: string }) => {
const {
setup: { components },
account: { account },
} = useDojo();

@@ -38,7 +38,14 @@ export const WorldStructuresMenu = ({ className }: { className?: string }) => {

const { hyperstructures } = useHyperstructures();
const { fragmentMines } = useFragmentMines();
const myHyperstructures = useGetHyperstructuresWithContributionsFromPlayer();

const myHyperstructures = useMemo(
() =>
LeaderboardManager.instance(components).getHyperstructuresWithContributionsFromPlayer(
ContractAddress(account.address),
),
[components, account.address],
);

const renderExtraContent = useCallback(
(entityId: ID, type: "hyperstructure" | "fragmentMine") => {
@@ -86,7 +93,7 @@ export const WorldStructuresMenu = ({ className }: { className?: string }) => {
position: { x: h.x, y: h.y },
...h,
}))}
filterEntityIds={showOnlyMine ? Array.from(myHyperstructures()) : undefined}
filterEntityIds={showOnlyMine ? Array.from(myHyperstructures) : undefined}
/>
</>
),
Loading