Skip to content

Commit 810ec6e

Browse files
committed
[TOOL-4651] Dashboard: Show error on invalid token distribution in ERC20 asset creation form (#7260)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on enhancing the `TokenDistributionFieldset` component by adding error handling for token distribution and improving the UI by displaying error messages. It also updates the `StepCard` and `DistributionBarChart` components to accommodate new features. ### Detailed summary - Added `disabled` prop to `StepCard`. - Integrated error handling in `TokenDistributionFieldset` for distribution validation. - Displayed error messages when distribution exceeds supply. - Improved `DistributionBarChart` to conditionally style text based on segment percentages. - Introduced `getDistributionError` and `SafeNumber` utility functions for validation logic. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added validation to ensure token distribution does not exceed total supply, with error messages displayed when limits are surpassed. - The "Next" submit button in the token creation flow can now be disabled based on validation errors. - **Bug Fixes** - Owner percentage in the distribution chart is now prevented from displaying negative values. - **Style** - Segment labels in the distribution chart legend are visually highlighted if their percentage is outside the 0–100% range. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 1c6ed9b commit 810ec6e

File tree

3 files changed

+55
-5
lines changed

3 files changed

+55
-5
lines changed

apps/dashboard/src/@/components/blocks/distribution-chart.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type DistributionBarChartProps = {
1010
segments: Segment[];
1111
title: string;
1212
};
13+
1314
export function DistributionBarChart(props: DistributionBarChartProps) {
1415
const totalPercentage = props.segments.reduce(
1516
(sum, segment) => sum + segment.percent,
@@ -59,7 +60,13 @@ export function DistributionBarChart(props: DistributionBarChartProps) {
5960
backgroundColor: segment.color,
6061
}}
6162
/>
62-
<p className="text-sm">
63+
<p
64+
className={cn(
65+
"text-sm",
66+
(segment.percent > 100 || segment.percent < 0) &&
67+
"text-destructive-text",
68+
)}
69+
>
6370
{segment.label}: {segment.percent}%
6471
</p>
6572
</div>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/create-token-card.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export function StepCard(props: {
1515
| undefined
1616
| {
1717
type: "submit";
18+
disabled?: boolean;
1819
}
1920
| {
2021
type: "custom";
@@ -56,6 +57,7 @@ export function StepCard(props: {
5657
variant="default"
5758
className="gap-2"
5859
type="submit"
60+
disabled={props.nextButton.disabled}
5961
onClick={() => {
6062
trackEvent(
6163
getStepCardTrackingData({

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/assets/create/distribution/token-distribution.tsx

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export function TokenDistributionFieldset(props: {
2626
tokenSymbol: string | undefined;
2727
}) {
2828
const { form } = props;
29+
const distributionError = getDistributionError(form);
2930

3031
return (
3132
<Form {...form}>
@@ -38,6 +39,7 @@ export function TokenDistributionFieldset(props: {
3839
}}
3940
nextButton={{
4041
type: "submit",
42+
disabled: !!distributionError,
4143
}}
4244
>
4345
<div>
@@ -56,9 +58,17 @@ export function TokenDistributionFieldset(props: {
5658
</div>
5759
</FormFieldSetup>
5860

59-
<TokenDistributionBarChart
60-
distributionFormValues={form.watch()}
61-
/>
61+
<div className="flex flex-col gap-3">
62+
<TokenDistributionBarChart
63+
distributionFormValues={form.watch()}
64+
/>
65+
66+
{distributionError && (
67+
<div className="text-destructive-text text-sm">
68+
{distributionError}
69+
</div>
70+
)}
71+
</div>
6272
</div>
6373

6474
<TokenAirdropSection form={form} client={props.client} />
@@ -74,6 +84,36 @@ export function TokenDistributionFieldset(props: {
7484
);
7585
}
7686

87+
function getDistributionError(form: TokenDistributionForm) {
88+
const supply = Number(form.watch("supply"));
89+
const totalAirdrop = form.watch("airdropAddresses").reduce((sum, addr) => {
90+
return sum + SafeNumber(addr.quantity);
91+
}, 0);
92+
93+
if (totalAirdrop > supply) {
94+
return "Total airdrop quantity exceeds total supply";
95+
}
96+
97+
const saleSupplyPercentage = SafeNumber(
98+
form.watch("saleAllocationPercentage"),
99+
);
100+
101+
const saleSupply = (saleSupplyPercentage / 100) * supply;
102+
const ownerSupply = Math.max(supply - totalAirdrop - saleSupply, 0);
103+
const totalSumOfSupply = totalAirdrop + saleSupply + ownerSupply;
104+
105+
if (totalSumOfSupply > supply) {
106+
return "Token distribution exceeds total supply";
107+
}
108+
109+
return undefined;
110+
}
111+
112+
function SafeNumber(value: string) {
113+
const num = Number(value);
114+
return Number.isNaN(num) ? 0 : num;
115+
}
116+
77117
export function TokenDistributionBarChart(props: {
78118
distributionFormValues: TokenDistributionFormValues;
79119
}) {
@@ -87,7 +127,8 @@ export function TokenDistributionBarChart(props: {
87127
const salePercentage = Number(
88128
props.distributionFormValues.saleAllocationPercentage,
89129
);
90-
const ownerPercentage = 100 - airdropPercentage - salePercentage;
130+
131+
const ownerPercentage = Math.max(100 - airdropPercentage - salePercentage, 0);
91132

92133
const tokenAllocations: Segment[] = [
93134
{

0 commit comments

Comments
 (0)