Skip to content

Commit

Permalink
fix: Refine validation and fee calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
carina-akaia committed Jun 23, 2024
1 parent b2787ab commit ca20b69
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 127 deletions.
2 changes: 1 addition & 1 deletion src/common/ui/form-fields/text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface TextFieldProps
fieldExtension?: React.ReactNode;
appendix?: string | null;
description?: string;
customErrorMessage?: string;
customErrorMessage?: string | null;
}

export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
Expand Down
123 changes: 37 additions & 86 deletions src/modules/donation/components/DonationBreakdown.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { UseFormReturn } from "react-hook-form";

import { NEAR_TOKEN_DENOM } from "@/common/constants";
import { FormField } from "@/common/ui/components";
import { CheckboxField } from "@/common/ui/form-fields";
import { TokenIcon } from "@/modules/core";
import { ProfileLink } from "@/modules/profile";

import { useDonationFees } from "../hooks/fees";
import { DonationInputs } from "../models";
Expand All @@ -23,7 +20,6 @@ export const DonationBreakdown: React.FC<DonationBreakdownProps> = ({
projectAllocationPercent,
protocolFeeAmount,
protocolFeePercent,
protocolFeeRecipientAccountId,
referrerFeeAmount,
referrerFeePercent,
chefFeeAmount,
Expand Down Expand Up @@ -66,91 +62,46 @@ export const DonationBreakdown: React.FC<DonationBreakdownProps> = ({
];

return (
<>
<div un-flex="~ col" un-gap="2">
<span className="prose" un-text="neutral-600" un-font="600">
Breakdown
</span>

<div
un-flex="~ col"
un-gap="2"
un-p="4"
un-border="~ neutral-300 rounded-lg"
>
{computedTotalFees.map(
({
display = true,
label,
amount,
percentage,
tokenId = values.tokenId,
}) =>
display && (
<div un-flex="~" un-justify="between" un-gap="4" key={label}>
<span className="prose">
{label + (percentage ? ` (${percentage}%)` : "")}
</span>

<span className="flex items-center gap-2">
<span
className="prose line-height-none"
un-font="600"
un-mt="0.6"
>
{amount}
</span>

<TokenIcon {...{ tokenId }} size="small" />
<div un-flex="~ col" un-gap="2">
<span className="prose" un-text="neutral-600" un-font="600">
Breakdown
</span>

<div
un-flex="~ col"
un-gap="2"
un-p="4"
un-border="~ neutral-300 rounded-lg"
>
{computedTotalFees.map(
({
display = true,
label,
amount,
percentage,
tokenId = values.tokenId,
}) =>
display && (
<div un-flex="~" un-justify="between" un-gap="4" key={label}>
<span className="prose">
{label + (percentage ? ` (${percentage}%)` : "")}
</span>

<span className="flex items-center gap-2">
<span
className="prose line-height-none"
un-font="600"
un-mt="0.6"
>
{amount}
</span>
</div>
),
)}
</div>
</div>

<div className="flex flex-col gap-2">
{protocolFeeRecipientAccountId && (
<FormField
control={form.control}
name="bypassProtocolFee"
render={({ field }) => (
<CheckboxField
checked={field.value}
onCheckedChange={field.onChange}
label={
<>
<span className="prose">{`Remove ${protocolFeePercent}% Protocol Fees`}</span>
<ProfileLink accountId={protocolFeeRecipientAccountId} />
</>
}
/>
)}
/>
)}

{values.potAccountId && (
<FormField
control={form.control}
name="bypassProtocolFee"
render={({ field }) => (
<CheckboxField
checked={field.value}
onCheckedChange={field.onChange}
label={
<>
<span className="prose">{`Remove ${chefFeePercent}% Chef Fees`}</span>

{values.potAccountId && (
<ProfileLink accountId={values.potAccountId} />
)}
</>
}
/>
)}
/>
<TokenIcon {...{ tokenId }} size="small" />
</span>
</div>
),
)}
</div>
</>
</div>
);
};
49 changes: 49 additions & 0 deletions src/modules/donation/components/DonationConfirmation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ import {
FormLabel,
Textarea,
} from "@/common/ui/components";
import { CheckboxField } from "@/common/ui/form-fields";
import { cn } from "@/common/ui/utils";
import { TokenIcon, useNearUsdDisplayValue } from "@/modules/core";
import { ProfileLink } from "@/modules/profile";

import { DonationBreakdown } from "./DonationBreakdown";
import { useDonationFees } from "../hooks/fees";
import { DonationInputs } from "../models";

export type DonationConfirmationProps = {
Expand All @@ -32,6 +35,9 @@ export const DonationConfirmation: React.FC<DonationConfirmationProps> = ({
const values = form.watch();
const [isMessageFieldVisible, setIsMessageFieldVisible] = useState(false);

const { protocolFeePercent, protocolFeeRecipientAccountId, chefFeePercent } =
useDonationFees(values);

const onAddNoteClick = useCallback(() => {
setIsMessageFieldVisible(true);
form.setValue("message", "", { shouldDirty: true });
Expand Down Expand Up @@ -90,6 +96,49 @@ export const DonationConfirmation: React.FC<DonationConfirmationProps> = ({

<DonationBreakdown {...{ form }} />

<div className="flex flex-col gap-2">
{protocolFeeRecipientAccountId && (
<FormField
control={form.control}
name="bypassProtocolFee"
render={({ field }) => (
<CheckboxField
checked={field.value}
onCheckedChange={field.onChange}
label={
<>
<span className="prose">{`Remove ${protocolFeePercent}% Protocol Fees`}</span>
<ProfileLink accountId={protocolFeeRecipientAccountId} />
</>
}
/>
)}
/>
)}

{values.potAccountId && (
<FormField
control={form.control}
name="bypassProtocolFee"
render={({ field }) => (
<CheckboxField
checked={field.value}
onCheckedChange={field.onChange}
label={
<>
<span className="prose">{`Remove ${chefFeePercent}% Chef Fees`}</span>

{values.potAccountId && (
<ProfileLink accountId={values.potAccountId} />
)}
</>
}
/>
)}
/>
)}
</div>

{values.recipientAccountId && (
<FormField
control={form.control}
Expand Down
26 changes: 20 additions & 6 deletions src/modules/donation/components/DonationFlow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,23 @@ export const DonationFlow: React.FC<DonationFlowProps> = ({
}) => {
const searchParams = useSearchParams();

const { isBalanceSufficient, form, isDisabled, onSubmit } = useDonationForm({
...props,
referrerAccountId: searchParams.get("referrerId") ?? undefined,
});
const { isBalanceSufficient, minAmountError, form, isDisabled, onSubmit } =
useDonationForm({
...props,
referrerAccountId: searchParams.get("referrerId") ?? undefined,
});

const [tokenId] = form.watch(["tokenId"]);

const { balanceFloat } = useAvailableBalance({ tokenId });

const content = useMemo(() => {
const staticAllocationProps = { isBalanceSufficient, balanceFloat, form };
const staticAllocationProps = {
isBalanceSufficient,
minAmountError,
balanceFloat,
form,
};

switch (currentStep) {
case "allocation":
Expand All @@ -58,7 +64,15 @@ export const DonationFlow: React.FC<DonationFlowProps> = ({
/>
);
}
}, [balanceFloat, closeModal, currentStep, form, isBalanceSufficient, props]);
}, [
balanceFloat,
closeModal,
currentStep,
form,
isBalanceSufficient,
minAmountError,
props,
]);

return (
<Form {...form}>
Expand Down
15 changes: 12 additions & 3 deletions src/modules/donation/components/DonationProjectAllocation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
useNearUsdDisplayValue,
} from "@/modules/core";

import { DONATION_MIN_NEAR_AMOUNT } from "../constants";
import {
DonationAllocationInputs,
DonationAllocationStrategyEnum,
Expand All @@ -40,7 +41,13 @@ export type DonationProjectAllocationProps = ByAccountId &

export const DonationProjectAllocation: React.FC<
DonationProjectAllocationProps
> = ({ isBalanceSufficient, accountId, balanceFloat, form }) => {
> = ({
isBalanceSufficient,
minAmountError,
accountId,
balanceFloat,
form,
}) => {
const [amount, tokenId, allocationStrategy] = form.watch([
"amount",
"tokenId",
Expand Down Expand Up @@ -170,13 +177,15 @@ export const DonationProjectAllocation: React.FC<
}
type="number"
placeholder="0.00"
min={0.0}
min={
tokenId === NEAR_TOKEN_DENOM ? DONATION_MIN_NEAR_AMOUNT : 0.0
}
max={balanceFloat ?? undefined}
step={0.01}
appendix={isFtDonation ? null : nearAmountUsdDisplayValue}
customErrorMessage={
isBalanceSufficient
? undefined
? minAmountError
: "You don’t have enough balance to complete this transaction."
}
/>
Expand Down
2 changes: 2 additions & 0 deletions src/modules/donation/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const DONATION_MIN_NEAR_AMOUNT = 0.1;

export const DONATION_MIN_NEAR_AMOUNT_ERROR = `The minimum donation amount is ${DONATION_MIN_NEAR_AMOUNT} NEAR`;

export const DONATION_MAX_MESSAGE_LENGTH = 100;
22 changes: 13 additions & 9 deletions src/modules/donation/hooks/fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,29 @@ export const useDonationFees = ({
}: DonationFeeInputs): DonationFees => {
const { data: potlockDonationConfig } = potlock.useDonationConfig();

const protocolFeeBasisPoints =
potlockDonationConfig?.protocol_fee_basis_points ?? 0;
const protocolFeeBasisPoints = bypassProtocolFee
? 0
: potlockDonationConfig?.protocol_fee_basis_points ?? 0;

const potlockReferralFeeBasisPoints =
potlockDonationConfig?.referral_fee_basis_points ?? 0;

const { data: potData } = potlock.usePot({ potId: potAccountId ?? "" });

const referralFeeBasisPoints =
potData?.referral_fee_public_round_basis_points ??
potlockReferralFeeBasisPoints;
const referralFeeBasisPoints = referrerAccountId
? 0
: potData?.referral_fee_public_round_basis_points ??
potlockReferralFeeBasisPoints;

const chefFeeBasisPoints = potData?.chef_fee_basis_points ?? 0;
const chefFeeBasisPoints = bypassChefFee
? 0
: potData?.chef_fee_basis_points ?? 0;

const projectAllocationBasisPoints =
TOTAL_FEE_BASIS_POINTS -
(bypassProtocolFee ? 0 : protocolFeeBasisPoints) -
(bypassChefFee ? 0 : chefFeeBasisPoints) -
(referrerAccountId ? referralFeeBasisPoints : 0);
protocolFeeBasisPoints -
chefFeeBasisPoints -
referralFeeBasisPoints;

return {
projectAllocationAmount:
Expand Down
Loading

0 comments on commit ca20b69

Please sign in to comment.