Skip to content

Commit

Permalink
fix: simplify subdenom creation (#182)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Felix C. Morency <[email protected]>
  • Loading branch information
chalabi2 and fmorency authored Jan 8, 2025
1 parent f984bf1 commit 8cdf6d2
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 43 deletions.
21 changes: 13 additions & 8 deletions components/factory/forms/ConfirmationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,19 @@ export default function ConfirmationForm({
const effectiveAddress =
formData.isGroup && formData.groupPolicyAddress ? formData.groupPolicyAddress : address;

const fullDenom = `factory/${effectiveAddress}/${formData.subdenom}`;
const getDenomInfo = (subdenom: string) => {
const prefixedSubdenom = 'u' + subdenom;
const symbol = (subdenom.startsWith('u') ? subdenom.slice(1) : subdenom).toUpperCase();
const fullDenom = `factory/${effectiveAddress}/${prefixedSubdenom}`;
return { prefixedSubdenom, symbol, fullDenom };
};

const { prefixedSubdenom, symbol, fullDenom } = getDenomInfo(formData.subdenom);

const handleConfirm = async () => {
setIsSigning(true);

const createAsGroup = async () => {
const symbol = formData.subdenom.slice(1).toUpperCase();
const msg = submitProposal({
groupPolicyAddress: formData.groupPolicyAddress || '',
messages: [
Expand All @@ -44,7 +50,7 @@ export default function ConfirmationForm({
value: MsgCreateDenom.encode(
createDenom({
sender: formData.groupPolicyAddress || '',
subdenom: formData.subdenom,
subdenom: prefixedSubdenom,
}).value
).finish(),
}),
Expand All @@ -59,10 +65,10 @@ export default function ConfirmationForm({
{
denom: fullDenom,
exponent: 0,
aliases: [symbol],
aliases: [formData.display],
},
{
denom: symbol,
denom: formData.display,
exponent: 6,
aliases: [fullDenom],
},
Expand Down Expand Up @@ -99,7 +105,7 @@ export default function ConfirmationForm({
// First, create the denom
const createDenomMsg = createDenom({
sender: address,
subdenom: formData.subdenom,
subdenom: prefixedSubdenom,
});

const createDenomFee = await estimateFee(address, [createDenomMsg]);
Expand All @@ -113,7 +119,6 @@ export default function ConfirmationForm({
return;
}

const symbol = formData.subdenom.slice(1).toUpperCase();
// If createDenom is successful, proceed with setDenomMetadata
const setMetadataMsg = setDenomMetadata({
sender: address,
Expand All @@ -126,7 +131,7 @@ export default function ConfirmationForm({
aliases: [symbol],
},
{
denom: symbol,
denom: formData.display,
exponent: 6,
aliases: [fullDenom],
},
Expand Down
37 changes: 29 additions & 8 deletions components/factory/forms/CreateDenom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@ export default function CreateDenom({
const DenomSchema = Yup.object().shape({
subdenom: Yup.string()
.required('Subdenom is required')
.matches(/^[u][a-zA-Z0-9]+$/, 'Subdenom must start with the letter u')
.min(4, 'Subdenom must be at least 4 characters')
.max(44, 'Subdenom must not exceed 44 characters')
.noProfanity('Profanity is not allowed')
.simulateDenomCreation(async () => {
setIsSimulating(true);
try {
return await simulateDenomCreation(formData.subdenom);
return await simulateDenomCreation(`u${formData.subdenom}`);
} finally {
setIsSimulating(false);
}
Expand All @@ -41,11 +40,32 @@ export default function CreateDenom({
<Formik
initialValues={formData}
validationSchema={DenomSchema}
onSubmit={nextStep}
validateOnChange={true}
validateOnBlur={true}
onSubmit={async (values, { setSubmitting, setErrors }) => {
try {
await DenomSchema.validate(values, { abortEarly: false });
nextStep();
} catch (err) {
if (err instanceof Yup.ValidationError) {
const errors = err.inner.reduce(
(acc: Record<string, string>, error) => ({
...acc,
[error.path as string]: error.message,
}),
{}
);
setErrors(errors);
} else {
console.error('Unexpected error:', err);
setErrors({ subdenom: 'An unexpected error occurred' });
}
} finally {
setSubmitting(false);
}
}}
validateOnChange={false}
validateOnBlur={false}
>
{({ setFieldValue, isValid, isSubmitting, isValidating, handleSubmit }) => (
{({ setFieldValue, isValid, isSubmitting, isValidating, handleSubmit, setErrors }) => (
<>
<div className="flex items-center mx-auto w-full dark:bg-[#FFFFFF0F] bg-[#FFFFFFCC] p-6 sm:p-8 rounded-2xl shadow-lg">
<div className="w-full">
Expand All @@ -57,8 +77,7 @@ export default function CreateDenom({
<TextInput
label="Token Sub Denom"
name="subdenom"
placeholder="utoken"
helperText="Use a subdenom starting with a prefix (e.g., 'utoken')"
placeholder="token"
value={formData.subdenom}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
dispatch({
Expand All @@ -67,6 +86,7 @@ export default function CreateDenom({
value: e.target.value,
});
setFieldValue('subdenom', e.target.value);
setErrors({});
}}
rightElement={
isSimulating && (
Expand Down Expand Up @@ -126,6 +146,7 @@ export default function CreateDenom({
<button
className="w-[calc(50%-12px)] btn px-5 py-2.5 sm:py-3.5 btn-gradient text-white disabled:text-black"
onClick={() => handleSubmit()}
type="submit"
disabled={!isValid || isSubmitting || isValidating}
>
Next: Token Metadata
Expand Down
10 changes: 5 additions & 5 deletions components/factory/forms/TokenDetailsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,17 @@ export default function TokenDetails({
const effectiveAddress =
formData.isGroup && formData.groupPolicyAddress ? formData.groupPolicyAddress : address;

const fullDenom = `factory/${effectiveAddress}/${formData.subdenom}`;

const fullDenom = `factory/${effectiveAddress}/u${formData.subdenom}`;
console.log(formData);
// Automatically set denom units
React.useEffect(() => {
const denomUnits = [
{ denom: fullDenom, exponent: 0, aliases: [] },
{ denom: formData.subdenom.slice(1), exponent: 6, aliases: [] },
{ denom: formData.display, exponent: 6, aliases: [] },
];
updateField('denomUnits', denomUnits);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [formData.subdenom, address]);
}, [formData.subdenom, formData.display, address]);

return (
<section>
Expand All @@ -78,7 +78,7 @@ export default function TokenDetails({
label="Subdenom"
name="subdenom"
disabled={true}
value={formData.subdenom}
value={`u${formData.subdenom}`}
aria-label="Token subdenom"
/>
<TextInput
Expand Down
24 changes: 2 additions & 22 deletions components/factory/forms/__tests__/CreateDenom.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('CreateDenom Component', () => {

test('updates subdenom input correctly', async () => {
renderWithChainProvider(<CreateDenom {...mockProps} />);
const subdenomInput = screen.getByPlaceholderText('utoken');
const subdenomInput = screen.getByPlaceholderText('token');
fireEvent.change(subdenomInput, { target: { value: 'utest' } });
await waitFor(() => {
expect(mockProps.dispatch).toHaveBeenCalledWith({
Expand All @@ -43,30 +43,10 @@ describe('CreateDenom Component', () => {
});
});

test('shows validation error for invalid subdenom', async () => {
renderWithChainProvider(<CreateDenom {...mockProps} />);
const subdenomInput = screen.getByPlaceholderText('utoken');
fireEvent.change(subdenomInput, { target: { value: 'invalid' } });
fireEvent.blur(subdenomInput);
await waitFor(() => {
expect(screen.getByText('Subdenom must start with the letter u')).toBeInTheDocument();
});
});

test('confirm button is disabled when inputs are invalid', async () => {
renderWithChainProvider(<CreateDenom {...mockProps} />);
const confirmButton = screen.getByText('Next: Token Metadata');
const subdenomInput = screen.getByPlaceholderText('utoken');
fireEvent.change(subdenomInput, { target: { value: 'invalid' } });
await waitFor(() => {
expect(confirmButton).toBeDisabled();
});
});

test('confirm button is enabled when inputs are valid', async () => {
renderWithChainProvider(<CreateDenom {...mockProps} />);
const confirmButton = screen.getByText('Next: Token Metadata');
const subdenomInput = screen.getByPlaceholderText('utoken');
const subdenomInput = screen.getByPlaceholderText('token');
fireEvent.change(subdenomInput, { target: { value: 'utest' } });
fireEvent.blur(subdenomInput);
await waitFor(() => {
Expand Down

0 comments on commit 8cdf6d2

Please sign in to comment.