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

Fix forms and handle fees #348

Merged
merged 4 commits into from
Aug 13, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ export default function Page({
<section className="section-layout">
<div className="text-center sm:mt-5">
<h2 className="text-xl font-semibold leading-6 text-gray-900">
Create a Proposal in Pool
Create a Proposal in Pool #{poolId}
</h2>
<div className="mt-1">
<p className="text-sm">{metadata.title}</p>
<h4 className="text-sm">{metadata.title}</h4>
</div>
</div>
<ProposalForm
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export default function Page({
params: { chain: number; garden: string };
}) {
const { data: result, error: getCommunityCreationDataQueryError } =
useSubgraphQuery<getCommunityCreationDataQuery>({
query: getCommunityCreationDataDocument,
variables: { addr: garden },
});
useSubgraphQuery<getCommunityCreationDataQuery>({
query: getCommunityCreationDataDocument,
variables: { addr: garden },
});

useEffect(() => {
if (getCommunityCreationDataQueryError) {
Expand All @@ -36,32 +36,29 @@ export default function Page({
const alloContractAddr = result?.tokenGarden?.communities?.[0]
.alloAddress as Address;

return tokenGarden ? (
<div className="page-layout">
<section className="section-layout">
<div className="text-center sm:mt-5">
<h2 className="text-xl font-semibold leading-6 text-gray-900">
Welcome to the {tokenGarden.symbol} Community Form!
</h2>
<div className="mt-1">
<p className="text-sm">
Create a vibrant community around the{" "}
{tokenGarden.name} by providing the necessary
details below.
</p>
return tokenGarden ?
<div className="page-layout">
<section className="section-layout">
<div className="text-center sm:mt-5">
<h2 className="text-xl font-semibold leading-6 text-gray-900">
Welcome to the {tokenGarden.symbol} Community Form!
</h2>
<div className="mt-1">
<p className="text-sm">
Create a vibrant community around the {tokenGarden.name} by
providing the necessary details below.
</p>
</div>
</div>
</div>
</section>
<CommunityForm
chainId={chain}
tokenGarden={tokenGarden}
registryFactoryAddr={registryFactoryAddr}
alloContractAddr={alloContractAddr}
/>
</div>
) : (
<div className="mt-96">
<LoadingSpinner />
</div>
);
<CommunityForm
chainId={chain}
tokenGarden={tokenGarden}
registryFactoryAddr={registryFactoryAddr}
alloContractAddr={alloContractAddr}
/>
</section>
</div>
: <div className="mt-96">
<LoadingSpinner />
</div>;
}
110 changes: 29 additions & 81 deletions apps/web/components/Forms/CommunityForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { TokenGarden } from "#/subgraph/.graphclient";
import { FormCheckBox } from "./FormCheckBox";
import { FormInput } from "./FormInput";
import { FormPreview, FormRow } from "./FormPreview";
import { FormSelect, Option } from "./FormSelect";
import { Button } from "@/components";
import { getChain } from "@/configs/chainServer";
import { getConfigByChain } from "@/constants/contracts";
Expand All @@ -21,7 +20,10 @@ import { registryFactoryABI, safeABI } from "@/src/generated";
import { abiWithErrors } from "@/utils/abiWithErrors";
import { getEventFromReceipt } from "@/utils/contracts";
import { ipfsJsonUpload } from "@/utils/ipfsUtils";
import { SCALE_PRECISION_DECIMALS } from "@/utils/numbers";
import {
CV_PERCENTAGE_SCALE,
CV_PERCENTAGE_SCALE_DECIMALS,
} from "@/utils/numbers";

//protocol : 1 => means ipfs!, to do some checks later

Expand All @@ -42,11 +44,6 @@ type FormRowTypes = {
};

const ethereumAddressRegEx = /^(0x)?[0-9a-fA-F]{40}$/;
const feeOptions: Option[] = [
{ value: 0, label: "0%" },
{ value: 0.01, label: "1%" },
{ value: 0.02, label: "2%" },
];

export const CommunityForm = ({
chainId,
Expand Down Expand Up @@ -95,34 +92,12 @@ export const CommunityForm = ({
},
feeAmount: {
label: "Community Fee Amount:",
parse: (value: number) => `${value * 100 ?? "0"} %`,
parse: (value: number) => `${value} %`,
},
feeReceiver: { label: "Fee Receiver:" },
councilSafe: { label: "Council Safe:" },
};

// const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {
// if (!e.target.files) return;
// const selectedFile = e.target.files[0];

// const ipfsUpload = ipfsFileUpload(selectedFile);

// toast
// .promise(ipfsUpload, {
// pending: "Uploading image...",
// success: "Successfully uploaded!",
// error: "Try uploading banner image again",
// })
// .then((data) => {
// console.log("https://ipfs.io/ipfs/" + data);
// setFile(selectedFile);
// setIpfsFileHash(data);
// })
// .catch((error: any) => {
// console.error(error);
// });
// };

const createCommunity = async () => {
setLoading(true);
const json = {
Expand Down Expand Up @@ -182,8 +157,9 @@ export const CommunityForm = ({
);
const communityFeeAmount = parseUnits(
previewData?.feeAmount.toString() as string,
SCALE_PRECISION_DECIMALS,
CV_PERCENTAGE_SCALE_DECIMALS,
);

const communityFeeReceiver = previewData?.feeReceiver;
const councilSafeAddress = previewData?.councilSafe;
// arb safe 0xda7bdebd79833a5e0c027fab1b1b9b874ddcbd10
Expand Down Expand Up @@ -303,13 +279,32 @@ export const CommunityForm = ({
</FormInput>
</div>
<div className="flex flex-col">
<FormSelect
<FormInput
label="Community fee %"
register={register}
required
errors={errors}
registerKey="feeAmount"
options={feeOptions}
/>
type="number"
placeholder="0"
className="pr-14"
otherProps={{
step: 1 / CV_PERCENTAGE_SCALE,
min: 1 / CV_PERCENTAGE_SCALE,
}}
registerOptions={{
max: {
value: 100,
message: "Max amount cannot exceed 100%",
},
min: {
value: 1 / CV_PERCENTAGE_SCALE,
message: `Amount must be greater than ${1 / CV_PERCENTAGE_SCALE}`,
},
}}
>
<span className="absolute right-4 top-4 text-black">%</span>
</FormInput>
</div>
<div className="flex flex-col">
<FormInput
Expand Down Expand Up @@ -370,53 +365,6 @@ export const CommunityForm = ({
placeholder="Covenant description..."
/>
</div>

{/* Upload image */}
{/* <label htmlFor="cover-photo" className={labelClassname}>
Banner Image
</label>
<div className="mt-2 flex justify-center rounded-lg border border-dashed border-secondary px-6 py-10">
<div className="text-center">
{file ? (
<Image
src={URL.createObjectURL(file)}
alt="Project cover photo"
width={100}
height={100}
/>
) : (
<>
<div className="mt-4 flex flex-col text-sm leading-6 text-gray-400 ">
<PhotoIcon
className="mx-auto h-12 w-12 text-secondary"
aria-hidden="true"
/>
<label
htmlFor={"image"}
className="relative cursor-pointer rounded-lg bg-surface font-semibold transition-colors duration-200 ease-in-out focus-within:outline-none focus-within:ring-2 focus-within:ring-indigo-200 focus-within:ring-offset-2 focus-within:ring-offset-gray-900 hover:text-primary"
>
<span className="text-secondary">Upload a file</span>
<input
id={"image"}
name={"image"}
type="file"
className="sr-only"
accept="image/*"
onChange={(e) => setFile(e.target.files?[0])}
/>
</label>

<div className="mt-1 space-y-1">
<p className="pl-1 text-black">or drag and drop</p>
<p className="text-xs leading-5 text-black">
PNG, JPG, GIF up to 10MB
</p>
</div>
</div>
</>
)}
</div>
</div> */}
</div>
}
<div className="flex w-full items-center justify-center py-6">
Expand Down
37 changes: 35 additions & 2 deletions apps/web/components/Forms/ProposalForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,37 @@ const abiParameters = [

const ethereumAddressRegEx = /^(0x)?[0-9a-fA-F]{40}$/;

function formatNumber(num: string | number): string {
// Convert to number if it's a string
const number = typeof num === "string" ? parseFloat(num) : num;

// Check if the number is NaN
if (isNaN(number)) {
return "Invalid Number";
}

// If the absolute value is greater than or equal to 1, use toFixed(2)
if (Math.abs(number) >= 1) {
return number.toFixed(2);
}

// For numbers between 0 and 1 (exclusive)
const parts = number.toString().split("e");
const exponent = parts[1] ? parseInt(parts[1]) : 0;

if (exponent < -3) {
// For very small numbers, use exponential notation with 4 significant digits
return number.toPrecision(4);
} else {
// For numbers between 0.001 and 1, show at least 4 decimal places
const decimalPlaces = Math.max(
4,
-Math.floor(Math.log10(Math.abs(number))) + 3,
);
return number.toFixed(decimalPlaces);
}
}

export const ProposalForm = ({
poolId,
proposalType,
Expand Down Expand Up @@ -116,6 +147,7 @@ export const ProposalForm = ({
const spendingLimitString = formatTokenAmount(
spendingLimit,
tokenGarden?.decimals as number,
6,
);

const proposalTypeName = poolTypes[proposalType];
Expand Down Expand Up @@ -246,6 +278,7 @@ export const ProposalForm = ({

return formattedRows;
};

return (
<form onSubmit={handleSubmit(handlePreview)} className="w-full">
{showPreview ?
Expand All @@ -260,13 +293,13 @@ export const ProposalForm = ({
<div className="relative flex flex-col">
<FormInput
label="Requested amount"
subLabel={`Max ${spendingLimitString} ${tokenSymbol} (${spendingLimitPct.toFixed(2)}% of Pool Funds)`}
subLabel={`Max ${formatNumber(spendingLimitString)} ${tokenSymbol} (${spendingLimitPct.toFixed(2)}% of Pool Funds)`}
register={register}
required
registerOptions={{
max: {
value: spendingLimitNumber,
message: `Max amount cannot exceed ${spendingLimitString} ${tokenSymbol}`,
message: `Max amount cannot exceed ${formatNumber(spendingLimitString)} ${tokenSymbol}`,
},
min: {
value: INPUT_TOKEN_MIN_VALUE,
Expand Down
6 changes: 5 additions & 1 deletion apps/web/utils/numbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,17 @@ export function parseToken(value: dn.Dnum | string, compact?: boolean) {
function formatTokenAmount(
value: string | number | bigint | undefined,
decimals: number,
digits?: number,
) {
if (digits === undefined) {
digits = 2;
}
if (!value) {
return "0";
}
const num = [BigInt(value), decimals] as const;

return dn.format(num, { digits: 2 });
return dn.format(num, { digits: digits });
}

function calculateFees(
Expand Down
Loading