Skip to content

Commit

Permalink
Merge branch 'master' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
aziolek committed Mar 20, 2024
2 parents 378d171 + 5d821d6 commit 069804e
Show file tree
Hide file tree
Showing 15 changed files with 157 additions and 79 deletions.
2 changes: 1 addition & 1 deletion backend/app/infrastructure/routes/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def get(self):
),
"leftover": fields.String(
required=False,
description="The amount that will be used to increase staking and for other Octant related operations.",
description="The amount that will be used to increase staking and for other Octant related operations. Includes donations to projects that didn't reach the threshold.",
),
},
)
Expand Down
39 changes: 18 additions & 21 deletions client/src/components/Allocation/AllocationItem/AllocationItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
useMotionValueEvent,
useTransform,
} from 'framer-motion';
import React, { FC, memo, useEffect, useRef, useState } from 'react';
import debounce from 'lodash/debounce';
import React, { FC, memo, useCallback, useEffect, useRef, useState } from 'react';
import { useAccount } from 'wagmi';

import AllocationItemRewards from 'components/Allocation/AllocationItemRewards';
Expand All @@ -31,6 +32,7 @@ import {

import styles from './AllocationItem.module.scss';
import AllocationItemProps from './types';
import { getAdjustedValue } from './utils';

const AllocationItem: FC<AllocationItemProps> = ({
address,
Expand All @@ -47,7 +49,7 @@ const AllocationItem: FC<AllocationItemProps> = ({
rewardsProps,
}) => {
const { data: individualReward } = useIndividualReward();
const isGweiRange = (individualReward && individualReward < GWEI_5) ?? false;
const isGweiRange = individualReward! < GWEI_5;

const [isInputFocused, setIsInputFocused] = useState(false);
const { ipfsGateways } = env;
Expand All @@ -70,22 +72,17 @@ const AllocationItem: FC<AllocationItemProps> = ({
const isEpoch1 = currentEpoch === 1;
const isLoading = currentEpoch === undefined || isFetchingRewardsThreshold;

const getAdjustedValue = (newValue: string, operation: 'multiply' | 'divide') => {
if (isGweiRange && newValue) {
return (
operation === 'multiply'
? parseFloat(newValue) * 1000000000
: parseFloat(newValue) / 1000000000
)
.toLocaleString('fullwide', {
maximumSignificantDigits: 18,
useGrouping: false,
})
.replace(comma, '.');
}

return newValue;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
const onChangeCallback = useCallback(
debounce(
(newAllocationValue, isManualModeEnforced) => {
onChange(newAllocationValue, isManualModeEnforced);
},
250,
{ trailing: true },
),
[],
);

const _onChange = (newValue: string) => {
const valueComma = newValue.replace(comma, '.');
Expand All @@ -101,10 +98,10 @@ const AllocationItem: FC<AllocationItemProps> = ({
setLocalValue(valueComma);

if (!isError) {
onChange(
onChangeCallback(
{
address,
value: getAdjustedValue(valueComma, 'divide'),
value: getAdjustedValue(valueComma, isGweiRange, 'divide'),
},
true,
);
Expand All @@ -116,7 +113,7 @@ const AllocationItem: FC<AllocationItemProps> = ({
return;
}

setLocalValue(getAdjustedValue(value, 'multiply'));
setLocalValue(getAdjustedValue(value, isGweiRange, 'multiply'));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value, isDecisionWindowOpen, isError, isGweiRange]);

Expand Down
31 changes: 31 additions & 0 deletions client/src/components/Allocation/AllocationItem/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { getAdjustedValue } from './utils';

describe('getAdjustedValue', () => {
describe('multiply', () => {
it('properly handles 0.0000001 ETH => 100 GWEI', () => {
expect(getAdjustedValue('0.0000001', true, 'multiply')).toEqual('100');
});

it('properly handles 0.00000005 ETH => 50 GWEI', () => {
expect(getAdjustedValue('0.00000005', true, 'multiply')).toEqual('50');
});

it('properly handles 0.00000003 ETH => 30 GWEI', () => {
expect(getAdjustedValue('0.00000003', true, 'multiply')).toEqual('30');
});
});

describe('divide', () => {
it('properly handles 100 GWEI => 0.0000001 ETH', () => {
expect(getAdjustedValue('100', true, 'divide')).toEqual('0.0000001');
});

it('properly handles 50 GWEI => 0.00000005 ETH', () => {
expect(getAdjustedValue('50', true, 'divide')).toEqual('0.00000005');
});

it('properly handles 30 GWEI => 0.00000003 ETH', () => {
expect(getAdjustedValue('30', true, 'divide')).toEqual('0.00000003');
});
});
});
26 changes: 26 additions & 0 deletions client/src/components/Allocation/AllocationItem/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { formatUnitsBigInt } from 'utils/formatUnitsBigInt';
import { parseUnitsBigInt } from 'utils/parseUnitsBigInt';
import { comma } from 'utils/regExp';

export function getAdjustedValue(
newValue: string,
isGweiRange: boolean,
operation: 'multiply' | 'divide',
): string {
if (isGweiRange && newValue) {
const adjustedValueBigInt =
operation === 'multiply'
? parseUnitsBigInt(newValue) * 1000000000n
: parseUnitsBigInt(newValue) / 1000000000n;
return (
formatUnitsBigInt(adjustedValueBigInt)
// @ts-expect-error TS method collision.
.toLocaleString('fullwide', {
maximumSignificantDigits: 18,
useGrouping: false,
})
.replace(comma, '.')
);
}
return newValue;
}
44 changes: 34 additions & 10 deletions client/src/components/Metrics/MetricsEpoch/MetricsEpoch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,11 @@ const MetricsEpoch = (): ReactElement => {
const { isFetching: isFetchingProjectsIpfsWithRewards } = useProjectsIpfsWithRewards(
isDecisionWindowOpen && epoch === lastEpoch ? undefined : epoch,
);
const { isFetching: isFetchingProjectsDonors } = useProjectsDonors(
isDecisionWindowOpen && epoch === lastEpoch ? undefined : epoch,
);
const { isFetching: isFetchingProjectRewardsThreshold } = useProjectRewardsThreshold(
const { data: projectsDonors, isFetching: isFetchingProjectsDonors } = useProjectsDonors(
isDecisionWindowOpen && epoch === lastEpoch ? undefined : epoch,
);
const { data: projectRewardsThreshold, isFetching: isFetchingProjectRewardsThreshold } =
useProjectRewardsThreshold(isDecisionWindowOpen && epoch === lastEpoch ? undefined : epoch);
const { isFetching: isFetchingEpochLeverage } = useEpochLeverage(epoch);
const { data: epochAllocations, isFetching: isFetchingEpochAllocations } =
useEpochAllocations(epoch);
Expand All @@ -65,14 +64,29 @@ const MetricsEpoch = (): ReactElement => {
const { data: epochUnusedRewards, isFetching: isFetchingEpochUnusedRewards } =
useEpochUnusedRewards(epoch);

const ethBelowThreshold =
projectRewardsThreshold === undefined
? BigInt(0)
: Object.values(projectsDonors).reduce((acc, curr) => {
const projectSumOfDonations = curr.reduce((acc2, curr2) => {
return acc2 + curr2.amount;
}, BigInt(0));

if (projectSumOfDonations < projectRewardsThreshold) {
return acc + projectSumOfDonations;
}

return acc;
}, BigInt(0));

const patronsRewards = epochInfo?.patronsRewards || BigInt(0);
const sumOfDonations =
epochAllocations?.reduce((acc, curr) => acc + curr.amount, BigInt(0)) || BigInt(0);
const totalDonations = sumOfDonations + patronsRewards;
const totalUserDonationsWithPatronRewards = sumOfDonations + patronsRewards;
const unusedRewards = epochUnusedRewards?.value || BigInt(0);
const epochBudget = epochBudgets?.budgetsSum || BigInt(0);

const totalPersonal = epochBudget - totalDonations - unusedRewards;
const totalPersonal = epochBudget - totalUserDonationsWithPatronRewards - unusedRewards;

// All metrics should be visible in the same moment (design). Skeletons are visible to the end of fetching all needed data.
const isLoading =
Expand Down Expand Up @@ -102,16 +116,22 @@ const MetricsEpoch = (): ReactElement => {
<MetricsEpochGridTotalDonationsAndPersonal
className={styles.totalDonationsAndPersonal}
isLoading={isLoading}
totalDonations={totalDonations}
totalPersonal={totalPersonal}
totalUserDonationsWithPatronRewards={totalUserDonationsWithPatronRewards}
/>
<MetricsEpochGridDonationsVsPersonalAllocations
className={styles.donationsVsPersonal}
isLoading={isLoading}
totalDonations={totalDonations}
totalPersonal={totalPersonal}
totalUserDonationsWithPatronRewards={totalUserDonationsWithPatronRewards}
/>
<MetricsEpochGridFundsUsage
className={styles.fundsUsage}
ethBelowThreshold={ethBelowThreshold}
isLoading={isLoading}
totalUserDonationsWithPatronRewards={totalUserDonationsWithPatronRewards}
unusedRewards={unusedRewards}
/>
<MetricsEpochGridFundsUsage className={styles.fundsUsage} isLoading={isLoading} />
<MetricsEpochGridTotalUsers className={styles.totalUsers} isLoading={isLoading} />
<MetricsEpochGridPatrons className={styles.patrons} isLoading={isLoading} />
<MetricsEpochGridCurrentDonors className={styles.currentDonors} isLoading={isLoading} />
Expand All @@ -120,7 +140,11 @@ const MetricsEpoch = (): ReactElement => {
className={styles.unusedAndUnallocatedValue}
isLoading={isLoading}
/>
<MetricsEpochGridBelowThreshold className={styles.belowThreshold} isLoading={isLoading} />
<MetricsEpochGridBelowThreshold
className={styles.belowThreshold}
ethBelowThreshold={ethBelowThreshold}
isLoading={isLoading}
/>
</MetricsGrid>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import useProjectsDonors from 'hooks/queries/donors/useProjectsDonors';
import useCryptoValues from 'hooks/queries/useCryptoValues';
import useIsDecisionWindowOpen from 'hooks/queries/useIsDecisionWindowOpen';
import useMatchedProjectRewards from 'hooks/queries/useMatchedProjectRewards';
import useProjectRewardsThreshold from 'hooks/queries/useProjectRewardsThreshold';
import i18n from 'i18n';
import useSettingsStore from 'store/settings/store';

Expand All @@ -18,6 +17,7 @@ import MetricsEpochGridBelowThresholdProps from './types';
const MetricsEpochGridBelowThreshold: FC<MetricsEpochGridBelowThresholdProps> = ({
isLoading,
className,
ethBelowThreshold,
}) => {
const { t } = useTranslation('translation', { keyPrefix: 'views.metrics' });
const {
Expand All @@ -38,29 +38,10 @@ const MetricsEpochGridBelowThreshold: FC<MetricsEpochGridBelowThresholdProps> =
isDecisionWindowOpen && epoch === lastEpoch ? undefined : epoch,
);

const { data: projectRewardsThreshold } = useProjectRewardsThreshold(
isDecisionWindowOpen && epoch === lastEpoch ? undefined : epoch,
);

const projectsBelowThreshold =
Object.keys(projectsDonors).length -
(matchedProjectRewards?.filter(({ matched }) => matched !== 0n).length || 0);

const ethBelowThreshold =
projectRewardsThreshold === undefined
? BigInt(0)
: Object.values(projectsDonors).reduce((acc, curr) => {
const projectSumOfDonations = curr.reduce((acc2, curr2) => {
return acc2 + curr2.amount;
}, BigInt(0));

if (projectSumOfDonations < projectRewardsThreshold) {
return acc + projectSumOfDonations;
}

return acc;
}, BigInt(0));

const ethBelowThresholdToDisplay = getValuesToDisplay({
cryptoCurrency: 'ethereum',
cryptoValues,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export default interface MetricsEpochGridBelowThresholdProps {
className?: string;
ethBelowThreshold: bigint;
isLoading: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ import MetricsEpochGridDonationsVsPersonalAllocationsProps from './types';

const MetricsEpochGridDonationsVsPersonalAllocations: FC<
MetricsEpochGridDonationsVsPersonalAllocationsProps
> = ({ totalDonations, isLoading, totalPersonal, className }) => {
> = ({ totalUserDonationsWithPatronRewards, isLoading, totalPersonal, className }) => {
const { t } = useTranslation('translation', { keyPrefix: 'views.metrics' });

const totalDonationsNumber = parseFloat(formatUnitsBigInt(totalDonations));
const totalUserDonationWithPatronRewardsNumber = parseFloat(
formatUnitsBigInt(totalUserDonationsWithPatronRewards),
);
const totalPersonalNumber = parseFloat(formatUnitsBigInt(totalPersonal));

const donationsValue =
totalDonationsNumber > 0
? (totalDonationsNumber / (totalPersonalNumber + totalDonationsNumber)) * 100
totalUserDonationWithPatronRewardsNumber > 0
? (totalUserDonationWithPatronRewardsNumber /
(totalPersonalNumber + totalUserDonationWithPatronRewardsNumber)) *
100
: 0;

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default interface MetricsEpochGridDonationsVsPersonalAllocationsProps {
className?: string;
isLoading: boolean;
totalDonations: bigint;
totalPersonal: bigint;
totalUserDonationsWithPatronRewards: bigint;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import MetricsEpochGridFundsUsageProps from './types';
const MetricsEpochGridFundsUsage: FC<MetricsEpochGridFundsUsageProps> = ({
isLoading,
className,
totalUserDonationsWithPatronRewards,
unusedRewards,
ethBelowThreshold,
}) => {
const { t } = useTranslation('translation', { keyPrefix: 'views.metrics' });
const { epoch } = useMetricsEpoch();
Expand All @@ -26,17 +29,25 @@ const MetricsEpochGridFundsUsage: FC<MetricsEpochGridFundsUsageProps> = ({
const leftover = epochInfo ? epochInfo.leftover : BigInt(0);

const projectCosts = epochInfo ? epochInfo.operationalCost : BigInt(0);
const matchRewards = epochInfo ? epochInfo.matchedRewards : BigInt(0);
const userRewards = epochInfo ? epochInfo.individualRewards : BigInt(0);
const staking = epochInfo ? epochInfo.staking : BigInt(0);

const total = leftover + projectCosts + matchRewards + userRewards + staking;
const donatedToProjects = epochInfo
? epochInfo.matchedRewards +
(totalUserDonationsWithPatronRewards - epochInfo.patronsRewards) -
ethBelowThreshold
: BigInt(0);

const claimedByUsers = epochInfo
? epochInfo.individualRewards - totalUserDonationsWithPatronRewards - unusedRewards
: BigInt(0);

const total = claimedByUsers + donatedToProjects + projectCosts + staking + leftover;

const data = [
{
label: t('staking'),
value: getNumberValue(staking),
valueLabel: getFormattedEthValue(staking, true, false, false, 2).fullString,
label: t('donatedToProjects'),
value: getNumberValue(donatedToProjects),
valueLabel: getFormattedEthValue(donatedToProjects, true, false, false, 2).fullString,
},
{
label: t('leftover', { epochNumber: epoch + 1 }),
Expand All @@ -49,14 +60,14 @@ const MetricsEpochGridFundsUsage: FC<MetricsEpochGridFundsUsageProps> = ({
valueLabel: getFormattedEthValue(projectCosts, true, false, false, 2).fullString,
},
{
label: t('matchRewards'),
value: getNumberValue(matchRewards),
valueLabel: getFormattedEthValue(matchRewards, true, false, false, 2).fullString,
label: t('claimedByUsers'),
value: getNumberValue(claimedByUsers),
valueLabel: getFormattedEthValue(claimedByUsers, true, false, false, 2).fullString,
},
{
label: t('userRewards'),
value: getNumberValue(userRewards),
valueLabel: getFormattedEthValue(userRewards, true, false, false, 2).fullString,
label: t('staking'),
value: getNumberValue(staking),
valueLabel: getFormattedEthValue(staking, true, false, false, 2).fullString,
},
];

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export default interface MetricsEpochGridFundsUsageProps {
className?: string;
ethBelowThreshold: bigint;
isLoading: boolean;
totalUserDonationsWithPatronRewards: bigint;
unusedRewards: bigint;
}
Loading

0 comments on commit 069804e

Please sign in to comment.